
/*-------------------------------------------------------------------------
 *
 * outfuncs.c
 *	  Postgres树节点的输出函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/nodes/outfuncs.c
 *
 * NOTES
 *	  每种可以出现在存储规则解析树中的节点类型 *必须*
 *	  在这里定义输出函数（以及在readfuncs.c中的输入函数）。
 *	  此外，计划节点应该具有输入和输出函数，以便能够
 *	  发送到并行工作者。
 *
 *	  由于调试目的，我们还为出现在原始解析树和计划路径中的节点提供
 *	  输出功能。这些节点类型不需要具有输入函数。原始解析树的输出支持
 *	  也有些不完整；尤其是，实用语句几乎完全不支持。
 *	  尽管如此，我们还是尽量支持在原始SELECT中出现的所有内容。
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>

#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/extensible.h"
#include "nodes/pathnodes.h"
#include "nodes/plannodes.h"
#include "utils/datum.h"
#include "utils/rel.h"

static void fc_outChar(StringInfo fc_str, char fc_c);


/*
 * 宏以简化不同类型字段的输出。尽可能使用这些
 * 以减少愚蠢的拼写错误的机会。请注意，这些
 * 硬编码了关于 Out 例程中局部变量名称的约定。
 */

/* 写入节点类型的标签 */
#define WRITE_NODE_TYPE(nodelabel) \
	appendStringInfoString(fc_str, nodelabel)

/* 写入整数字段（任何写入为 ":fldname %d" 的内容） */
#define WRITE_INT_FIELD(fldname) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " %d", fc_node->fldname)

/* 写入无符号整数字段（任何写入为 ":fldname %u" 的内容） */
#define WRITE_UINT_FIELD(fldname) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " %u", fc_node->fldname)

/* 写入无符号整数字段（任何使用UINT64_FORMAT编写的内容） */
#define WRITE_UINT64_FIELD(fldname) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " " UINT64_FORMAT, \
					 fc_node->fldname)

/* 写入OID字段（不要硬编码OID与uint相同的假设） */
#define WRITE_OID_FIELD(fldname) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " %u", fc_node->fldname)

/* 写入长整数字段 */
#define WRITE_LONG_FIELD(fldname) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " %ld", fc_node->fldname)

/* 写入字符字段（即，一个ascii字符） */
#define WRITE_CHAR_FIELD(fldname) \
	(appendStringInfo(fc_str, " :" CppAsString(fldname) " "), \
	 fc_outChar(fc_str, fc_node->fldname))

/* 将枚举类型字段写为整数代码 */
#define WRITE_ENUM_FIELD(fldname, enumtype) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " %d", \
					 (int) fc_node->fldname)

/* 写入浮点数字段 --- 调用者必须提供格式以定义精度 */
#define WRITE_FLOAT_FIELD(fldname,format) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " " format, fc_node->fldname)

/* 写入布尔字段 */
#define WRITE_BOOL_FIELD(fldname) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " %s", \
					 booltostr(fc_node->fldname))

/* 写入字符字符串（可能为NULL）字段 */
#define WRITE_STRING_FIELD(fldname) \
	(appendStringInfoString(fc_str, " :" CppAsString(fldname) " "), \
	 outToken(fc_str, fc_node->fldname))

/* 写入解析位置字段（实际上与INT情况相同） */
#define WRITE_LOCATION_FIELD(fldname) \
	appendStringInfo(fc_str, " :" CppAsString(fldname) " %d", fc_node->fldname)

/* 写入节点字段 */
#define WRITE_NODE_FIELD(fldname) \
	(appendStringInfoString(fc_str, " :" CppAsString(fldname) " "), \
	 outNode(fc_str, fc_node->fldname))

/* 写入位图集字段 */
#define WRITE_BITMAPSET_FIELD(fldname) \
	(appendStringInfoString(fc_str, " :" CppAsString(fldname) " "), \
	 outBitmapset(fc_str, fc_node->fldname))

#define WRITE_ATTRNUMBER_ARRAY(fldname, len) \
	do { \
		appendStringInfoString(fc_str, " :" CppAsString(fldname) " "); \
		for (int fc_i = 0; fc_i < len; fc_i++) \
			appendStringInfo(fc_str, " %d", fc_node->fldname[fc_i]); \
	} while(0)

#define WRITE_OID_ARRAY(fldname, len) \
	do { \
		appendStringInfoString(fc_str, " :" CppAsString(fldname) " "); \
		for (int fc_i = 0; fc_i < len; fc_i++) \
			appendStringInfo(fc_str, " %u", fc_node->fldname[fc_i]); \
	} while(0)

/*
 * 这个宏支持字段为NULL的情况。对于其他数组
 * 宏，目前不需要这样做。
 */
#define WRITE_INDEX_ARRAY(fldname, len) \
	do { \
		appendStringInfoString(fc_str, " :" CppAsString(fldname) " "); \
		if (fc_node->fldname) \
			for (int fc_i = 0; fc_i < len; fc_i++) \
				appendStringInfo(fc_str, " %u", fc_node->fldname[fc_i]); \
		else \
			appendStringInfoString(fc_str, "<>"); \
	} while(0)

#define WRITE_INT_ARRAY(fldname, len) \
	do { \
		appendStringInfoString(fc_str, " :" CppAsString(fldname) " "); \
		for (int fc_i = 0; fc_i < len; fc_i++) \
			appendStringInfo(fc_str, " %d", fc_node->fldname[fc_i]); \
	} while(0)

#define WRITE_BOOL_ARRAY(fldname, len) \
	do { \
		appendStringInfoString(fc_str, " :" CppAsString(fldname) " "); \
		for (int fc_i = 0; fc_i < len; fc_i++) \
			appendStringInfo(fc_str, " %s", booltostr(fc_node->fldname[fc_i])); \
	} while(0)


#define booltostr(x)  ((x) ? "true" : "false")


/*
 * outToken
 *	  将普通字符串（例如，一个标识符）转换为一种形式，
 *	  该形式将通过read.c的函数解码为普通标记。
 *
 *	  如果给出null或空字符串，将其编码为“<>”。
 */
void outToken(StringInfo fc_str, const char *fc_s)
{
	if (fc_s == NULL || *fc_s == '\0')
	{
		appendStringInfoString(fc_str, "<>");
		return;
	}

	/*
	 * 查找被read.c（在pg_strtok()或nodeRead()中）特殊处理的字符或模式，
	 * 因此需要一个保护性的反斜杠。
	 */
	/* 这些字符仅在字符串的开始处需要引用 */
	if (*fc_s == '<' ||
		*fc_s == '"' ||
		isdigit((unsigned char) *fc_s) ||
		((*fc_s == '+' || *fc_s == '-') &&
		 (isdigit((unsigned char) fc_s[1]) || fc_s[1] == '.')))
		appendStringInfoChar(fc_str, '\\');
	while (*fc_s)
	{
		/* 这些字符必须在字符串中的任何位置加反斜杠 */
		if (*fc_s == ' ' || *fc_s == '\n' || *fc_s == '\t' ||
			*fc_s == '(' || *fc_s == ')' || *fc_s == '{' || *fc_s == '}' ||
			*fc_s == '\\')
			appendStringInfoChar(fc_str, '\\');
		appendStringInfoChar(fc_str, *fc_s++);
	}
}

/*
 * 转换一个字符。通过outToken()以便特殊字符被
 * 转义。
 */
static void fc_outChar(StringInfo fc_str, char fc_c)
{
	char		fc_in[2];

	fc_in[0] = fc_c;
	fc_in[1] = '\0';

	outToken(fc_str, fc_in);
}

static void fc__outList(StringInfo fc_str, const List *fc_node)
{
	const ListCell *fc_lc;

	appendStringInfoChar(fc_str, '(');

	if (IsA(fc_node, IntList))
		appendStringInfoChar(fc_str, 'i');
	else if (IsA(fc_node, OidList))
		appendStringInfoChar(fc_str, 'o');

	foreach(fc_lc, fc_node)
	{
		/*
		 * 为了向后兼容，我们为节点列表与其他类型的
		 * 列表发出稍微不同的空白格式。XXX：这有必要吗？
		 */
		if (IsA(fc_node, List))
		{
			outNode(fc_str, lfirst(fc_lc));
			if (lnext(fc_node, fc_lc))
				appendStringInfoChar(fc_str, ' ');
		}
		else if (IsA(fc_node, IntList))
			appendStringInfo(fc_str, " %d", lfirst_int(fc_lc));
		else if (IsA(fc_node, OidList))
			appendStringInfo(fc_str, " %u", lfirst_oid(fc_lc));
		else
			elog(ERROR, "unrecognized list node type: %d",
				 (int) fc_node->type);
	}

	appendStringInfoChar(fc_str, ')');
}

/*
 * outBitmapset -
 *	   转换一个整数的位图集
 *
 * 注意：输出格式为“(b int int ...)”，类似于整数列表。
 */
void outBitmapset(StringInfo fc_str, const Bitmapset *fc_bms)
{
	int			fc_x;

	appendStringInfoChar(fc_str, '(');
	appendStringInfoChar(fc_str, 'b');
	fc_x = -1;
	while ((fc_x = bms_next_member(fc_bms, fc_x)) >= 0)
		appendStringInfo(fc_str, " %d", fc_x);
	appendStringInfoChar(fc_str, ')');
}

/*
 * 打印给定类型的Datum的值。
 */
void outDatum(StringInfo fc_str, Datum fc_value, int fc_typlen, bool fc_typbyval)
{
	Size		fc_length,
				fc_i;
	char	   *fc_s;

	fc_length = datumGetSize(fc_value, fc_typbyval, fc_typlen);

	if (fc_typbyval)
	{
		fc_s = (char *) (&fc_value);
		appendStringInfo(fc_str, "%u [ ", (unsigned int) fc_length);
		for (fc_i = 0; fc_i < (Size) sizeof(Datum); fc_i++)
			appendStringInfo(fc_str, "%d ", (int) (fc_s[fc_i]));
		appendStringInfoChar(fc_str, ']');
	}
	else
	{
		fc_s = (char *) DatumGetPointer(fc_value);
		if (!PointerIsValid(fc_s))
			appendStringInfoString(fc_str, "0 [ ]");
		else
		{
			appendStringInfo(fc_str, "%u [ ", (unsigned int) fc_length);
			for (fc_i = 0; fc_i < fc_length; fc_i++)
				appendStringInfo(fc_str, "%d ", (int) (fc_s[fc_i]));
			appendStringInfoChar(fc_str, ']');
		}
	}
}


/*
 *	来自plannodes.h的内容
 */

static void fc__outPlannedStmt(StringInfo fc_str, const PlannedStmt *fc_node)
{
	WRITE_NODE_TYPE("PLANNEDSTMT");

	WRITE_ENUM_FIELD(commandType, CmdType);
	WRITE_UINT64_FIELD(queryId);
	WRITE_BOOL_FIELD(hasReturning);
	WRITE_BOOL_FIELD(hasModifyingCTE);
	WRITE_BOOL_FIELD(canSetTag);
	WRITE_BOOL_FIELD(transientPlan);
	WRITE_BOOL_FIELD(dependsOnRole);
	WRITE_BOOL_FIELD(parallelModeNeeded);
	WRITE_INT_FIELD(jitFlags);
	WRITE_NODE_FIELD(planTree);
	WRITE_NODE_FIELD(rtable);
	WRITE_NODE_FIELD(resultRelations);
	WRITE_NODE_FIELD(appendRelations);
	WRITE_NODE_FIELD(subplans);
	WRITE_BITMAPSET_FIELD(rewindPlanIDs);
	WRITE_NODE_FIELD(rowMarks);
	WRITE_NODE_FIELD(relationOids);
	WRITE_NODE_FIELD(invalItems);
	WRITE_NODE_FIELD(paramExecTypes);
	WRITE_NODE_FIELD(utilityStmt);
	WRITE_LOCATION_FIELD(stmt_location);
	WRITE_INT_FIELD(stmt_len);
}

/*
 * 打印所有继承自Plan的节点的基本内容
 */
static void fc__outPlanInfo(StringInfo fc_str, const Plan *fc_node)
{
	WRITE_FLOAT_FIELD(startup_cost, "%.2f");
	WRITE_FLOAT_FIELD(total_cost, "%.2f");
	WRITE_FLOAT_FIELD(plan_rows, "%.0f");
	WRITE_INT_FIELD(plan_width);
	WRITE_BOOL_FIELD(parallel_aware);
	WRITE_BOOL_FIELD(parallel_safe);
	WRITE_BOOL_FIELD(async_capable);
	WRITE_INT_FIELD(plan_node_id);
	WRITE_NODE_FIELD(targetlist);
	WRITE_NODE_FIELD(qual);
	WRITE_NODE_FIELD(lefttree);
	WRITE_NODE_FIELD(righttree);
	WRITE_NODE_FIELD(initPlan);
	WRITE_BITMAPSET_FIELD(extParam);
	WRITE_BITMAPSET_FIELD(allParam);
}

/*
 * 打印所有继承自Scan的节点的基本内容
 */
static void fc__outScanInfo(StringInfo fc_str, const Scan *fc_node)
{
	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_UINT_FIELD(scanrelid);
}

/*
 * 打印所有继承自Join的节点的基本内容
 */
static void fc__outJoinPlanInfo(StringInfo fc_str, const Join *fc_node)
{
	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_ENUM_FIELD(jointype, JoinType);
	WRITE_BOOL_FIELD(inner_unique);
	WRITE_NODE_FIELD(joinqual);
}


static void fc__outPlan(StringInfo fc_str, const Plan *fc_node)
{
	WRITE_NODE_TYPE("PLAN");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);
}

static void fc__outResult(StringInfo fc_str, const Result *fc_node)
{
	WRITE_NODE_TYPE("RESULT");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_NODE_FIELD(resconstantqual);
}

static void fc__outProjectSet(StringInfo fc_str, const ProjectSet *fc_node)
{
	WRITE_NODE_TYPE("PROJECTSET");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);
}

static void fc__outModifyTable(StringInfo fc_str, const ModifyTable *fc_node)
{
	WRITE_NODE_TYPE("MODIFYTABLE");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_ENUM_FIELD(operation, CmdType);
	WRITE_BOOL_FIELD(canSetTag);
	WRITE_UINT_FIELD(nominalRelation);
	WRITE_UINT_FIELD(rootRelation);
	WRITE_BOOL_FIELD(partColsUpdated);
	WRITE_NODE_FIELD(resultRelations);
	WRITE_NODE_FIELD(updateColnosLists);
	WRITE_NODE_FIELD(withCheckOptionLists);
	WRITE_NODE_FIELD(returningLists);
	WRITE_NODE_FIELD(fdwPrivLists);
	WRITE_BITMAPSET_FIELD(fdwDirectModifyPlans);
	WRITE_NODE_FIELD(rowMarks);
	WRITE_INT_FIELD(epqParam);
	WRITE_ENUM_FIELD(onConflictAction, OnConflictAction);
	WRITE_NODE_FIELD(arbiterIndexes);
	WRITE_NODE_FIELD(onConflictSet);
	WRITE_NODE_FIELD(onConflictCols);
	WRITE_NODE_FIELD(onConflictWhere);
	WRITE_UINT_FIELD(exclRelRTI);
	WRITE_NODE_FIELD(exclRelTlist);
	WRITE_NODE_FIELD(mergeActionLists);
}

static void fc__outAppend(StringInfo fc_str, const Append *fc_node)
{
	WRITE_NODE_TYPE("APPEND");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_BITMAPSET_FIELD(apprelids);
	WRITE_NODE_FIELD(appendplans);
	WRITE_INT_FIELD(nasyncplans);
	WRITE_INT_FIELD(first_partial_plan);
	WRITE_NODE_FIELD(part_prune_info);
}

static void fc__outMergeAppend(StringInfo fc_str, const MergeAppend *fc_node)
{
	WRITE_NODE_TYPE("MERGEAPPEND");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_BITMAPSET_FIELD(apprelids);
	WRITE_NODE_FIELD(mergeplans);
	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(sortColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(sortOperators, fc_node->numCols);
	WRITE_OID_ARRAY(collations, fc_node->numCols);
	WRITE_BOOL_ARRAY(nullsFirst, fc_node->numCols);
	WRITE_NODE_FIELD(part_prune_info);
}

static void fc__outRecursiveUnion(StringInfo fc_str, const RecursiveUnion *fc_node)
{
	WRITE_NODE_TYPE("RECURSIVEUNION");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_INT_FIELD(wtParam);
	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(dupColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(dupOperators, fc_node->numCols);
	WRITE_OID_ARRAY(dupCollations, fc_node->numCols);
	WRITE_LONG_FIELD(numGroups);
}

static void fc__outBitmapAnd(StringInfo fc_str, const BitmapAnd *fc_node)
{
	WRITE_NODE_TYPE("BITMAPAND");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_NODE_FIELD(bitmapplans);
}

static void fc__outBitmapOr(StringInfo fc_str, const BitmapOr *fc_node)
{
	WRITE_NODE_TYPE("BITMAPOR");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_BOOL_FIELD(isshared);
	WRITE_NODE_FIELD(bitmapplans);
}

static void fc__outGather(StringInfo fc_str, const Gather *fc_node)
{
	WRITE_NODE_TYPE("GATHER");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_INT_FIELD(num_workers);
	WRITE_INT_FIELD(rescan_param);
	WRITE_BOOL_FIELD(single_copy);
	WRITE_BOOL_FIELD(invisible);
	WRITE_BITMAPSET_FIELD(initParam);
}

static void fc__outGatherMerge(StringInfo fc_str, const GatherMerge *fc_node)
{
	WRITE_NODE_TYPE("GATHERMERGE");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_INT_FIELD(num_workers);
	WRITE_INT_FIELD(rescan_param);
	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(sortColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(sortOperators, fc_node->numCols);
	WRITE_OID_ARRAY(collations, fc_node->numCols);
	WRITE_BOOL_ARRAY(nullsFirst, fc_node->numCols);
	WRITE_BITMAPSET_FIELD(initParam);
}

static void fc__outScan(StringInfo fc_str, const Scan *fc_node)
{
	WRITE_NODE_TYPE("SCAN");

	fc__outScanInfo(fc_str, fc_node);
}

static void fc__outSeqScan(StringInfo fc_str, const SeqScan *fc_node)
{
	WRITE_NODE_TYPE("SEQSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);
}

static void fc__outSampleScan(StringInfo fc_str, const SampleScan *fc_node)
{
	WRITE_NODE_TYPE("SAMPLESCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(tablesample);
}

static void fc__outIndexScan(StringInfo fc_str, const IndexScan *fc_node)
{
	WRITE_NODE_TYPE("INDEXSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_OID_FIELD(indexid);
	WRITE_NODE_FIELD(indexqual);
	WRITE_NODE_FIELD(indexqualorig);
	WRITE_NODE_FIELD(indexorderby);
	WRITE_NODE_FIELD(indexorderbyorig);
	WRITE_NODE_FIELD(indexorderbyops);
	WRITE_ENUM_FIELD(indexorderdir, ScanDirection);
}

static void fc__outIndexOnlyScan(StringInfo fc_str, const IndexOnlyScan *fc_node)
{
	WRITE_NODE_TYPE("INDEXONLYSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_OID_FIELD(indexid);
	WRITE_NODE_FIELD(indexqual);
	WRITE_NODE_FIELD(recheckqual);
	WRITE_NODE_FIELD(indexorderby);
	WRITE_NODE_FIELD(indextlist);
	WRITE_ENUM_FIELD(indexorderdir, ScanDirection);
}

static void fc__outBitmapIndexScan(StringInfo fc_str, const BitmapIndexScan *fc_node)
{
	WRITE_NODE_TYPE("BITMAPINDEXSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_OID_FIELD(indexid);
	WRITE_BOOL_FIELD(isshared);
	WRITE_NODE_FIELD(indexqual);
	WRITE_NODE_FIELD(indexqualorig);
}

static void fc__outBitmapHeapScan(StringInfo fc_str, const BitmapHeapScan *fc_node)
{
	WRITE_NODE_TYPE("BITMAPHEAPSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(bitmapqualorig);
}

static void fc__outTidScan(StringInfo fc_str, const TidScan *fc_node)
{
	WRITE_NODE_TYPE("TIDSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(tidquals);
}

static void fc__outTidRangeScan(StringInfo fc_str, const TidRangeScan *fc_node)
{
	WRITE_NODE_TYPE("TIDRANGESCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(tidrangequals);
}

static void fc__outSubqueryScan(StringInfo fc_str, const SubqueryScan *fc_node)
{
	WRITE_NODE_TYPE("SUBQUERYSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(subplan);
	WRITE_ENUM_FIELD(scanstatus, SubqueryScanStatus);
}

static void fc__outFunctionScan(StringInfo fc_str, const FunctionScan *fc_node)
{
	WRITE_NODE_TYPE("FUNCTIONSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(functions);
	WRITE_BOOL_FIELD(funcordinality);
}

static void fc__outTableFuncScan(StringInfo fc_str, const TableFuncScan *fc_node)
{
	WRITE_NODE_TYPE("TABLEFUNCSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(tablefunc);
}

static void fc__outValuesScan(StringInfo fc_str, const ValuesScan *fc_node)
{
	WRITE_NODE_TYPE("VALUESSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_NODE_FIELD(values_lists);
}

static void fc__outCteScan(StringInfo fc_str, const CteScan *fc_node)
{
	WRITE_NODE_TYPE("CTESCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_INT_FIELD(ctePlanId);
	WRITE_INT_FIELD(cteParam);
}

static void fc__outNamedTuplestoreScan(StringInfo fc_str, const NamedTuplestoreScan *fc_node)
{
	WRITE_NODE_TYPE("NAMEDTUPLESTORESCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_STRING_FIELD(enrname);
}

static void fc__outWorkTableScan(StringInfo fc_str, const WorkTableScan *fc_node)
{
	WRITE_NODE_TYPE("WORKTABLESCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_INT_FIELD(wtParam);
}

static void fc__outForeignScan(StringInfo fc_str, const ForeignScan *fc_node)
{
	WRITE_NODE_TYPE("FOREIGNSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_ENUM_FIELD(operation, CmdType);
	WRITE_UINT_FIELD(resultRelation);
	WRITE_OID_FIELD(fs_server);
	WRITE_NODE_FIELD(fdw_exprs);
	WRITE_NODE_FIELD(fdw_private);
	WRITE_NODE_FIELD(fdw_scan_tlist);
	WRITE_NODE_FIELD(fdw_recheck_quals);
	WRITE_BITMAPSET_FIELD(fs_relids);
	WRITE_BOOL_FIELD(fsSystemCol);
}

static void fc__outCustomScan(StringInfo fc_str, const CustomScan *fc_node)
{
	WRITE_NODE_TYPE("CUSTOMSCAN");

	fc__outScanInfo(fc_str, (const Scan *) fc_node);

	WRITE_UINT_FIELD(flags);
	WRITE_NODE_FIELD(custom_plans);
	WRITE_NODE_FIELD(custom_exprs);
	WRITE_NODE_FIELD(custom_private);
	WRITE_NODE_FIELD(custom_scan_tlist);
	WRITE_BITMAPSET_FIELD(custom_relids);
	/* CustomName是查找CustomScanMethods的关键字 */
	appendStringInfoString(fc_str, " :methods ");
	outToken(fc_str, fc_node->methods->CustomName);
}

static void fc__outJoin(StringInfo fc_str, const Join *fc_node)
{
	WRITE_NODE_TYPE("JOIN");

	fc__outJoinPlanInfo(fc_str, (const Join *) fc_node);
}

static void fc__outNestLoop(StringInfo fc_str, const NestLoop *fc_node)
{
	WRITE_NODE_TYPE("NESTLOOP");

	fc__outJoinPlanInfo(fc_str, (const Join *) fc_node);

	WRITE_NODE_FIELD(nestParams);
}

static void fc__outMergeJoin(StringInfo fc_str, const MergeJoin *fc_node)
{
	int			fc_numCols;

	WRITE_NODE_TYPE("MERGEJOIN");

	fc__outJoinPlanInfo(fc_str, (const Join *) fc_node);

	WRITE_BOOL_FIELD(skip_mark_restore);
	WRITE_NODE_FIELD(mergeclauses);

	fc_numCols = list_length(fc_node->mergeclauses);

	WRITE_OID_ARRAY(mergeFamilies, fc_numCols);
	WRITE_OID_ARRAY(mergeCollations, fc_numCols);
	WRITE_INT_ARRAY(mergeStrategies, fc_numCols);
	WRITE_BOOL_ARRAY(mergeNullsFirst, fc_numCols);
}

static void fc__outHashJoin(StringInfo fc_str, const HashJoin *fc_node)
{
	WRITE_NODE_TYPE("HASHJOIN");

	fc__outJoinPlanInfo(fc_str, (const Join *) fc_node);

	WRITE_NODE_FIELD(hashclauses);
	WRITE_NODE_FIELD(hashoperators);
	WRITE_NODE_FIELD(hashcollations);
	WRITE_NODE_FIELD(hashkeys);
}

static void fc__outAgg(StringInfo fc_str, const Agg *fc_node)
{
	WRITE_NODE_TYPE("AGG");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_ENUM_FIELD(aggstrategy, AggStrategy);
	WRITE_ENUM_FIELD(aggsplit, AggSplit);
	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(grpColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(grpOperators, fc_node->numCols);
	WRITE_OID_ARRAY(grpCollations, fc_node->numCols);
	WRITE_LONG_FIELD(numGroups);
	WRITE_UINT64_FIELD(transitionSpace);
	WRITE_BITMAPSET_FIELD(aggParams);
	WRITE_NODE_FIELD(groupingSets);
	WRITE_NODE_FIELD(chain);
}

static void fc__outWindowAgg(StringInfo fc_str, const WindowAgg *fc_node)
{
	WRITE_NODE_TYPE("WINDOWAGG");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_UINT_FIELD(winref);
	WRITE_INT_FIELD(partNumCols);
	WRITE_ATTRNUMBER_ARRAY(partColIdx, fc_node->partNumCols);
	WRITE_OID_ARRAY(partOperators, fc_node->partNumCols);
	WRITE_OID_ARRAY(partCollations, fc_node->partNumCols);
	WRITE_INT_FIELD(ordNumCols);
	WRITE_ATTRNUMBER_ARRAY(ordColIdx, fc_node->ordNumCols);
	WRITE_OID_ARRAY(ordOperators, fc_node->ordNumCols);
	WRITE_OID_ARRAY(ordCollations, fc_node->ordNumCols);
	WRITE_INT_FIELD(frameOptions);
	WRITE_NODE_FIELD(startOffset);
	WRITE_NODE_FIELD(endOffset);
	WRITE_NODE_FIELD(runCondition);
	WRITE_NODE_FIELD(runConditionOrig);
	WRITE_OID_FIELD(startInRangeFunc);
	WRITE_OID_FIELD(endInRangeFunc);
	WRITE_OID_FIELD(inRangeColl);
	WRITE_BOOL_FIELD(inRangeAsc);
	WRITE_BOOL_FIELD(inRangeNullsFirst);
	WRITE_BOOL_FIELD(topWindow);
}

static void fc__outGroup(StringInfo fc_str, const Group *fc_node)
{
	WRITE_NODE_TYPE("GROUP");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(grpColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(grpOperators, fc_node->numCols);
	WRITE_OID_ARRAY(grpCollations, fc_node->numCols);
}

static void fc__outMaterial(StringInfo fc_str, const Material *fc_node)
{
	WRITE_NODE_TYPE("MATERIAL");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);
}

static void fc__outMemoize(StringInfo fc_str, const Memoize *fc_node)
{
	WRITE_NODE_TYPE("MEMOIZE");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_INT_FIELD(numKeys);
	WRITE_OID_ARRAY(hashOperators, fc_node->numKeys);
	WRITE_OID_ARRAY(collations, fc_node->numKeys);
	WRITE_NODE_FIELD(param_exprs);
	WRITE_BOOL_FIELD(singlerow);
	WRITE_BOOL_FIELD(binary_mode);
	WRITE_UINT_FIELD(est_entries);
	WRITE_BITMAPSET_FIELD(keyparamids);
}

static void fc__outSortInfo(StringInfo fc_str, const Sort *fc_node)
{
	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(sortColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(sortOperators, fc_node->numCols);
	WRITE_OID_ARRAY(collations, fc_node->numCols);
	WRITE_BOOL_ARRAY(nullsFirst, fc_node->numCols);
}

static void fc__outSort(StringInfo fc_str, const Sort *fc_node)
{
	WRITE_NODE_TYPE("SORT");

	fc__outSortInfo(fc_str, fc_node);
}

static void fc__outIncrementalSort(StringInfo fc_str, const IncrementalSort *fc_node)
{
	WRITE_NODE_TYPE("INCREMENTALSORT");

	fc__outSortInfo(fc_str, (const Sort *) fc_node);

	WRITE_INT_FIELD(nPresortedCols);
}

static void fc__outUnique(StringInfo fc_str, const Unique *fc_node)
{
	WRITE_NODE_TYPE("UNIQUE");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(uniqColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(uniqOperators, fc_node->numCols);
	WRITE_OID_ARRAY(uniqCollations, fc_node->numCols);
}

static void fc__outHash(StringInfo fc_str, const Hash *fc_node)
{
	WRITE_NODE_TYPE("HASH");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_NODE_FIELD(hashkeys);
	WRITE_OID_FIELD(skewTable);
	WRITE_INT_FIELD(skewColumn);
	WRITE_BOOL_FIELD(skewInherit);
	WRITE_FLOAT_FIELD(rows_total, "%.0f");
}

static void fc__outSetOp(StringInfo fc_str, const SetOp *fc_node)
{
	WRITE_NODE_TYPE("SETOP");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_ENUM_FIELD(cmd, SetOpCmd);
	WRITE_ENUM_FIELD(strategy, SetOpStrategy);
	WRITE_INT_FIELD(numCols);
	WRITE_ATTRNUMBER_ARRAY(dupColIdx, fc_node->numCols);
	WRITE_OID_ARRAY(dupOperators, fc_node->numCols);
	WRITE_OID_ARRAY(dupCollations, fc_node->numCols);
	WRITE_INT_FIELD(flagColIdx);
	WRITE_INT_FIELD(firstFlag);
	WRITE_LONG_FIELD(numGroups);
}

static void fc__outLockRows(StringInfo fc_str, const LockRows *fc_node)
{
	WRITE_NODE_TYPE("LOCKROWS");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_NODE_FIELD(rowMarks);
	WRITE_INT_FIELD(epqParam);
}

static void fc__outLimit(StringInfo fc_str, const Limit *fc_node)
{
	WRITE_NODE_TYPE("LIMIT");

	fc__outPlanInfo(fc_str, (const Plan *) fc_node);

	WRITE_NODE_FIELD(limitOffset);
	WRITE_NODE_FIELD(limitCount);
	WRITE_ENUM_FIELD(limitOption, LimitOption);
	WRITE_INT_FIELD(uniqNumCols);
	WRITE_ATTRNUMBER_ARRAY(uniqColIdx, fc_node->uniqNumCols);
	WRITE_OID_ARRAY(uniqOperators, fc_node->uniqNumCols);
	WRITE_OID_ARRAY(uniqCollations, fc_node->uniqNumCols);
}

static void fc__outNestLoopParam(StringInfo fc_str, const NestLoopParam *fc_node)
{
	WRITE_NODE_TYPE("NESTLOOPPARAM");

	WRITE_INT_FIELD(paramno);
	WRITE_NODE_FIELD(paramval);
}

static void fc__outPlanRowMark(StringInfo fc_str, const PlanRowMark *fc_node)
{
	WRITE_NODE_TYPE("PLANROWMARK");

	WRITE_UINT_FIELD(rti);
	WRITE_UINT_FIELD(prti);
	WRITE_UINT_FIELD(rowmarkId);
	WRITE_ENUM_FIELD(markType, RowMarkType);
	WRITE_INT_FIELD(allMarkTypes);
	WRITE_ENUM_FIELD(strength, LockClauseStrength);
	WRITE_ENUM_FIELD(waitPolicy, LockWaitPolicy);
	WRITE_BOOL_FIELD(isParent);
}

static void fc__outPartitionPruneInfo(StringInfo fc_str, const PartitionPruneInfo *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONPRUNEINFO");

	WRITE_NODE_FIELD(prune_infos);
	WRITE_BITMAPSET_FIELD(other_subplans);
}

static void fc__outPartitionedRelPruneInfo(StringInfo fc_str, const PartitionedRelPruneInfo *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONEDRELPRUNEINFO");

	WRITE_UINT_FIELD(rtindex);
	WRITE_BITMAPSET_FIELD(present_parts);
	WRITE_INT_FIELD(nparts);
	WRITE_INT_ARRAY(subplan_map, fc_node->nparts);
	WRITE_INT_ARRAY(subpart_map, fc_node->nparts);
	WRITE_OID_ARRAY(relid_map, fc_node->nparts);
	WRITE_NODE_FIELD(initial_pruning_steps);
	WRITE_NODE_FIELD(exec_pruning_steps);
	WRITE_BITMAPSET_FIELD(execparamids);
}

static void fc__outPartitionPruneStepOp(StringInfo fc_str, const PartitionPruneStepOp *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONPRUNESTEPOP");

	WRITE_INT_FIELD(step.step_id);
	WRITE_INT_FIELD(opstrategy);
	WRITE_NODE_FIELD(exprs);
	WRITE_NODE_FIELD(cmpfns);
	WRITE_BITMAPSET_FIELD(nullkeys);
}

static void fc__outPartitionPruneStepCombine(StringInfo fc_str, const PartitionPruneStepCombine *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONPRUNESTEPCOMBINE");

	WRITE_INT_FIELD(step.step_id);
	WRITE_ENUM_FIELD(combineOp, PartitionPruneCombineOp);
	WRITE_NODE_FIELD(source_stepids);
}

static void fc__outPlanInvalItem(StringInfo fc_str, const PlanInvalItem *fc_node)
{
	WRITE_NODE_TYPE("PLANINVALITEM");

	WRITE_INT_FIELD(cacheId);
	WRITE_UINT_FIELD(hashValue);
}

/*****************************************************************************
 *
 *	来自primnodes.h的内容。
 *
 *****************************************************************************/

static void fc__outAlias(StringInfo fc_str, const Alias *fc_node)
{
	WRITE_NODE_TYPE("ALIAS");

	WRITE_STRING_FIELD(aliasname);
	WRITE_NODE_FIELD(colnames);
}

static void fc__outRangeVar(StringInfo fc_str, const RangeVar *fc_node)
{
	WRITE_NODE_TYPE("RANGEVAR");

	/*
	 * 我们故意忽略catalogname，因为目前它没有
	 * 语义意义
	 */
	WRITE_STRING_FIELD(schemaname);
	WRITE_STRING_FIELD(relname);
	WRITE_BOOL_FIELD(inh);
	WRITE_CHAR_FIELD(relpersistence);
	WRITE_NODE_FIELD(alias);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outTableFunc(StringInfo fc_str, const TableFunc *fc_node)
{
	WRITE_NODE_TYPE("TABLEFUNC");

	WRITE_NODE_FIELD(ns_uris);
	WRITE_NODE_FIELD(ns_names);
	WRITE_NODE_FIELD(docexpr);
	WRITE_NODE_FIELD(rowexpr);
	WRITE_NODE_FIELD(colnames);
	WRITE_NODE_FIELD(coltypes);
	WRITE_NODE_FIELD(coltypmods);
	WRITE_NODE_FIELD(colcollations);
	WRITE_NODE_FIELD(colexprs);
	WRITE_NODE_FIELD(coldefexprs);
	WRITE_BITMAPSET_FIELD(notnulls);
	WRITE_INT_FIELD(ordinalitycol);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outIntoClause(StringInfo fc_str, const IntoClause *fc_node)
{
	WRITE_NODE_TYPE("INTOCLAUSE");

	WRITE_NODE_FIELD(rel);
	WRITE_NODE_FIELD(colNames);
	WRITE_STRING_FIELD(accessMethod);
	WRITE_NODE_FIELD(options);
	WRITE_ENUM_FIELD(onCommit, OnCommitAction);
	WRITE_STRING_FIELD(tableSpaceName);
	WRITE_NODE_FIELD(viewQuery);
	WRITE_BOOL_FIELD(skipData);
}

static void fc__outVar(StringInfo fc_str, const Var *fc_node)
{
	WRITE_NODE_TYPE("VAR");

	WRITE_INT_FIELD(varno);
	WRITE_INT_FIELD(varattno);
	WRITE_OID_FIELD(vartype);
	WRITE_INT_FIELD(vartypmod);
	WRITE_OID_FIELD(varcollid);
	WRITE_UINT_FIELD(varlevelsup);
	WRITE_UINT_FIELD(varnosyn);
	WRITE_INT_FIELD(varattnosyn);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outConst(StringInfo fc_str, const Const *fc_node)
{
	WRITE_NODE_TYPE("CONST");

	WRITE_OID_FIELD(consttype);
	WRITE_INT_FIELD(consttypmod);
	WRITE_OID_FIELD(constcollid);
	WRITE_INT_FIELD(constlen);
	WRITE_BOOL_FIELD(constbyval);
	WRITE_BOOL_FIELD(constisnull);
	WRITE_LOCATION_FIELD(location);

	appendStringInfoString(fc_str, " :constvalue ");
	if (fc_node->constisnull)
		appendStringInfoString(fc_str, "<>");
	else
		outDatum(fc_str, fc_node->constvalue, fc_node->constlen, fc_node->constbyval);
}

static void fc__outParam(StringInfo fc_str, const Param *fc_node)
{
	WRITE_NODE_TYPE("PARAM");

	WRITE_ENUM_FIELD(paramkind, ParamKind);
	WRITE_INT_FIELD(paramid);
	WRITE_OID_FIELD(paramtype);
	WRITE_INT_FIELD(paramtypmod);
	WRITE_OID_FIELD(paramcollid);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outAggref(StringInfo fc_str, const Aggref *fc_node)
{
	WRITE_NODE_TYPE("AGGREF");

	WRITE_OID_FIELD(aggfnoid);
	WRITE_OID_FIELD(aggtype);
	WRITE_OID_FIELD(aggcollid);
	WRITE_OID_FIELD(inputcollid);
	WRITE_OID_FIELD(aggtranstype);
	WRITE_NODE_FIELD(aggargtypes);
	WRITE_NODE_FIELD(aggdirectargs);
	WRITE_NODE_FIELD(args);
	WRITE_NODE_FIELD(aggorder);
	WRITE_NODE_FIELD(aggdistinct);
	WRITE_NODE_FIELD(aggfilter);
	WRITE_BOOL_FIELD(aggstar);
	WRITE_BOOL_FIELD(aggvariadic);
	WRITE_CHAR_FIELD(aggkind);
	WRITE_UINT_FIELD(agglevelsup);
	WRITE_ENUM_FIELD(aggsplit, AggSplit);
	WRITE_INT_FIELD(aggno);
	WRITE_INT_FIELD(aggtransno);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outGroupingFunc(StringInfo fc_str, const GroupingFunc *fc_node)
{
	WRITE_NODE_TYPE("GROUPINGFUNC");

	WRITE_NODE_FIELD(args);
	WRITE_NODE_FIELD(refs);
	WRITE_NODE_FIELD(cols);
	WRITE_UINT_FIELD(agglevelsup);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outWindowFunc(StringInfo fc_str, const WindowFunc *fc_node)
{
	WRITE_NODE_TYPE("WINDOWFUNC");

	WRITE_OID_FIELD(winfnoid);
	WRITE_OID_FIELD(wintype);
	WRITE_OID_FIELD(wincollid);
	WRITE_OID_FIELD(inputcollid);
	WRITE_NODE_FIELD(args);
	WRITE_NODE_FIELD(aggfilter);
	WRITE_UINT_FIELD(winref);
	WRITE_BOOL_FIELD(winstar);
	WRITE_BOOL_FIELD(winagg);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outSubscriptingRef(StringInfo fc_str, const SubscriptingRef *fc_node)
{
	WRITE_NODE_TYPE("SUBSCRIPTINGREF");

	WRITE_OID_FIELD(refcontainertype);
	WRITE_OID_FIELD(refelemtype);
	WRITE_OID_FIELD(refrestype);
	WRITE_INT_FIELD(reftypmod);
	WRITE_OID_FIELD(refcollid);
	WRITE_NODE_FIELD(refupperindexpr);
	WRITE_NODE_FIELD(reflowerindexpr);
	WRITE_NODE_FIELD(refexpr);
	WRITE_NODE_FIELD(refassgnexpr);
}

static void fc__outFuncExpr(StringInfo fc_str, const FuncExpr *fc_node)
{
	WRITE_NODE_TYPE("FUNCEXPR");

	WRITE_OID_FIELD(funcid);
	WRITE_OID_FIELD(funcresulttype);
	WRITE_BOOL_FIELD(funcretset);
	WRITE_BOOL_FIELD(funcvariadic);
	WRITE_ENUM_FIELD(funcformat, CoercionForm);
	WRITE_OID_FIELD(funccollid);
	WRITE_OID_FIELD(inputcollid);
	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outNamedArgExpr(StringInfo fc_str, const NamedArgExpr *fc_node)
{
	WRITE_NODE_TYPE("NAMEDARGEXPR");

	WRITE_NODE_FIELD(arg);
	WRITE_STRING_FIELD(name);
	WRITE_INT_FIELD(argnumber);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outOpExpr(StringInfo fc_str, const OpExpr *fc_node)
{
	WRITE_NODE_TYPE("OPEXPR");

	WRITE_OID_FIELD(opno);
	WRITE_OID_FIELD(opfuncid);
	WRITE_OID_FIELD(opresulttype);
	WRITE_BOOL_FIELD(opretset);
	WRITE_OID_FIELD(opcollid);
	WRITE_OID_FIELD(inputcollid);
	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outDistinctExpr(StringInfo fc_str, const DistinctExpr *fc_node)
{
	WRITE_NODE_TYPE("DISTINCTEXPR");

	WRITE_OID_FIELD(opno);
	WRITE_OID_FIELD(opfuncid);
	WRITE_OID_FIELD(opresulttype);
	WRITE_BOOL_FIELD(opretset);
	WRITE_OID_FIELD(opcollid);
	WRITE_OID_FIELD(inputcollid);
	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outNullIfExpr(StringInfo fc_str, const NullIfExpr *fc_node)
{
	WRITE_NODE_TYPE("NULLIFEXPR");

	WRITE_OID_FIELD(opno);
	WRITE_OID_FIELD(opfuncid);
	WRITE_OID_FIELD(opresulttype);
	WRITE_BOOL_FIELD(opretset);
	WRITE_OID_FIELD(opcollid);
	WRITE_OID_FIELD(inputcollid);
	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outScalarArrayOpExpr(StringInfo fc_str, const ScalarArrayOpExpr *fc_node)
{
	WRITE_NODE_TYPE("SCALARARRAYOPEXPR");

	WRITE_OID_FIELD(opno);
	WRITE_OID_FIELD(opfuncid);
	WRITE_OID_FIELD(hashfuncid);
	WRITE_OID_FIELD(negfuncid);
	WRITE_BOOL_FIELD(useOr);
	WRITE_OID_FIELD(inputcollid);
	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outBoolExpr(StringInfo fc_str, const BoolExpr *fc_node)
{
	char	   *fc_opstr = NULL;

	WRITE_NODE_TYPE("BOOLEXPR");

	/* 自定义枚举表示法 */
	switch (fc_node->boolop)
	{
		case AND_EXPR:
			fc_opstr = "and";
			break;
		case OR_EXPR:
			fc_opstr = "or";
			break;
		case NOT_EXPR:
			fc_opstr = "not";
			break;
	}
	appendStringInfoString(fc_str, " :boolop ");
	outToken(fc_str, fc_opstr);

	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outSubLink(StringInfo fc_str, const SubLink *fc_node)
{
	WRITE_NODE_TYPE("SUBLINK");

	WRITE_ENUM_FIELD(subLinkType, SubLinkType);
	WRITE_INT_FIELD(subLinkId);
	WRITE_NODE_FIELD(testexpr);
	WRITE_NODE_FIELD(operName);
	WRITE_NODE_FIELD(subselect);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outSubPlan(StringInfo fc_str, const SubPlan *fc_node)
{
	WRITE_NODE_TYPE("SUBPLAN");

	WRITE_ENUM_FIELD(subLinkType, SubLinkType);
	WRITE_NODE_FIELD(testexpr);
	WRITE_NODE_FIELD(paramIds);
	WRITE_INT_FIELD(plan_id);
	WRITE_STRING_FIELD(plan_name);
	WRITE_OID_FIELD(firstColType);
	WRITE_INT_FIELD(firstColTypmod);
	WRITE_OID_FIELD(firstColCollation);
	WRITE_BOOL_FIELD(useHashTable);
	WRITE_BOOL_FIELD(unknownEqFalse);
	WRITE_BOOL_FIELD(parallel_safe);
	WRITE_NODE_FIELD(setParam);
	WRITE_NODE_FIELD(parParam);
	WRITE_NODE_FIELD(args);
	WRITE_FLOAT_FIELD(startup_cost, "%.2f");
	WRITE_FLOAT_FIELD(per_call_cost, "%.2f");
}

static void fc__outAlternativeSubPlan(StringInfo fc_str, const AlternativeSubPlan *fc_node)
{
	WRITE_NODE_TYPE("ALTERNATIVESUBPLAN");

	WRITE_NODE_FIELD(subplans);
}

static void fc__outFieldSelect(StringInfo fc_str, const FieldSelect *fc_node)
{
	WRITE_NODE_TYPE("FIELDSELECT");

	WRITE_NODE_FIELD(arg);
	WRITE_INT_FIELD(fieldnum);
	WRITE_OID_FIELD(resulttype);
	WRITE_INT_FIELD(resulttypmod);
	WRITE_OID_FIELD(resultcollid);
}

static void fc__outFieldStore(StringInfo fc_str, const FieldStore *fc_node)
{
	WRITE_NODE_TYPE("FIELDSTORE");

	WRITE_NODE_FIELD(arg);
	WRITE_NODE_FIELD(newvals);
	WRITE_NODE_FIELD(fieldnums);
	WRITE_OID_FIELD(resulttype);
}

static void fc__outRelabelType(StringInfo fc_str, const RelabelType *fc_node)
{
	WRITE_NODE_TYPE("RELABELTYPE");

	WRITE_NODE_FIELD(arg);
	WRITE_OID_FIELD(resulttype);
	WRITE_INT_FIELD(resulttypmod);
	WRITE_OID_FIELD(resultcollid);
	WRITE_ENUM_FIELD(relabelformat, CoercionForm);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCoerceViaIO(StringInfo fc_str, const CoerceViaIO *fc_node)
{
	WRITE_NODE_TYPE("COERCEVIAIO");

	WRITE_NODE_FIELD(arg);
	WRITE_OID_FIELD(resulttype);
	WRITE_OID_FIELD(resultcollid);
	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outArrayCoerceExpr(StringInfo fc_str, const ArrayCoerceExpr *fc_node)
{
	WRITE_NODE_TYPE("ARRAYCOERCEEXPR");

	WRITE_NODE_FIELD(arg);
	WRITE_NODE_FIELD(elemexpr);
	WRITE_OID_FIELD(resulttype);
	WRITE_INT_FIELD(resulttypmod);
	WRITE_OID_FIELD(resultcollid);
	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outConvertRowtypeExpr(StringInfo fc_str, const ConvertRowtypeExpr *fc_node)
{
	WRITE_NODE_TYPE("CONVERTROWTYPEEXPR");

	WRITE_NODE_FIELD(arg);
	WRITE_OID_FIELD(resulttype);
	WRITE_ENUM_FIELD(convertformat, CoercionForm);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCollateExpr(StringInfo fc_str, const CollateExpr *fc_node)
{
	WRITE_NODE_TYPE("COLLATEEXPR");

	WRITE_NODE_FIELD(arg);
	WRITE_OID_FIELD(collOid);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCaseExpr(StringInfo fc_str, const CaseExpr *fc_node)
{
	WRITE_NODE_TYPE("CASEEXPR");

	WRITE_OID_FIELD(casetype);
	WRITE_OID_FIELD(casecollid);
	WRITE_NODE_FIELD(arg);
	WRITE_NODE_FIELD(args);
	WRITE_NODE_FIELD(defresult);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCaseWhen(StringInfo fc_str, const CaseWhen *fc_node)
{
	WRITE_NODE_TYPE("CASEWHEN");

	WRITE_NODE_FIELD(expr);
	WRITE_NODE_FIELD(result);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCaseTestExpr(StringInfo fc_str, const CaseTestExpr *fc_node)
{
	WRITE_NODE_TYPE("CASETESTEXPR");

	WRITE_OID_FIELD(typeId);
	WRITE_INT_FIELD(typeMod);
	WRITE_OID_FIELD(collation);
}

static void fc__outArrayExpr(StringInfo fc_str, const ArrayExpr *fc_node)
{
	WRITE_NODE_TYPE("ARRAYEXPR");

	WRITE_OID_FIELD(array_typeid);
	WRITE_OID_FIELD(array_collid);
	WRITE_OID_FIELD(element_typeid);
	WRITE_NODE_FIELD(elements);
	WRITE_BOOL_FIELD(multidims);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outRowExpr(StringInfo fc_str, const RowExpr *fc_node)
{
	WRITE_NODE_TYPE("ROWEXPR");

	WRITE_NODE_FIELD(args);
	WRITE_OID_FIELD(row_typeid);
	WRITE_ENUM_FIELD(row_format, CoercionForm);
	WRITE_NODE_FIELD(colnames);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outRowCompareExpr(StringInfo fc_str, const RowCompareExpr *fc_node)
{
	WRITE_NODE_TYPE("ROWCOMPAREEXPR");

	WRITE_ENUM_FIELD(rctype, RowCompareType);
	WRITE_NODE_FIELD(opnos);
	WRITE_NODE_FIELD(opfamilies);
	WRITE_NODE_FIELD(inputcollids);
	WRITE_NODE_FIELD(largs);
	WRITE_NODE_FIELD(rargs);
}

static void fc__outCoalesceExpr(StringInfo fc_str, const CoalesceExpr *fc_node)
{
	WRITE_NODE_TYPE("COALESCEEXPR");

	WRITE_OID_FIELD(coalescetype);
	WRITE_OID_FIELD(coalescecollid);
	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outMinMaxExpr(StringInfo fc_str, const MinMaxExpr *fc_node)
{
	WRITE_NODE_TYPE("MINMAXEXPR");

	WRITE_OID_FIELD(minmaxtype);
	WRITE_OID_FIELD(minmaxcollid);
	WRITE_OID_FIELD(inputcollid);
	WRITE_ENUM_FIELD(op, MinMaxOp);
	WRITE_NODE_FIELD(args);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outSQLValueFunction(StringInfo fc_str, const SQLValueFunction *fc_node)
{
	WRITE_NODE_TYPE("SQLVALUEFUNCTION");

	WRITE_ENUM_FIELD(op, SQLValueFunctionOp);
	WRITE_OID_FIELD(type);
	WRITE_INT_FIELD(typmod);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outXmlExpr(StringInfo fc_str, const XmlExpr *fc_node)
{
	WRITE_NODE_TYPE("XMLEXPR");

	WRITE_ENUM_FIELD(op, XmlExprOp);
	WRITE_STRING_FIELD(name);
	WRITE_NODE_FIELD(named_args);
	WRITE_NODE_FIELD(arg_names);
	WRITE_NODE_FIELD(args);
	WRITE_ENUM_FIELD(xmloption, XmlOptionType);
	WRITE_OID_FIELD(type);
	WRITE_INT_FIELD(typmod);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outNullTest(StringInfo fc_str, const NullTest *fc_node)
{
	WRITE_NODE_TYPE("NULLTEST");

	WRITE_NODE_FIELD(arg);
	WRITE_ENUM_FIELD(nulltesttype, NullTestType);
	WRITE_BOOL_FIELD(argisrow);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outBooleanTest(StringInfo fc_str, const BooleanTest *fc_node)
{
	WRITE_NODE_TYPE("BOOLEANTEST");

	WRITE_NODE_FIELD(arg);
	WRITE_ENUM_FIELD(booltesttype, BoolTestType);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCoerceToDomain(StringInfo fc_str, const CoerceToDomain *fc_node)
{
	WRITE_NODE_TYPE("COERCETODOMAIN");

	WRITE_NODE_FIELD(arg);
	WRITE_OID_FIELD(resulttype);
	WRITE_INT_FIELD(resulttypmod);
	WRITE_OID_FIELD(resultcollid);
	WRITE_ENUM_FIELD(coercionformat, CoercionForm);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCoerceToDomainValue(StringInfo fc_str, const CoerceToDomainValue *fc_node)
{
	WRITE_NODE_TYPE("COERCETODOMAINVALUE");

	WRITE_OID_FIELD(typeId);
	WRITE_INT_FIELD(typeMod);
	WRITE_OID_FIELD(collation);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outSetToDefault(StringInfo fc_str, const SetToDefault *fc_node)
{
	WRITE_NODE_TYPE("SETTODEFAULT");

	WRITE_OID_FIELD(typeId);
	WRITE_INT_FIELD(typeMod);
	WRITE_OID_FIELD(collation);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCurrentOfExpr(StringInfo fc_str, const CurrentOfExpr *fc_node)
{
	WRITE_NODE_TYPE("CURRENTOFEXPR");

	WRITE_UINT_FIELD(cvarno);
	WRITE_STRING_FIELD(cursor_name);
	WRITE_INT_FIELD(cursor_param);
}

static void fc__outNextValueExpr(StringInfo fc_str, const NextValueExpr *fc_node)
{
	WRITE_NODE_TYPE("NEXTVALUEEXPR");

	WRITE_OID_FIELD(seqid);
	WRITE_OID_FIELD(typeId);
}

static void fc__outInferenceElem(StringInfo fc_str, const InferenceElem *fc_node)
{
	WRITE_NODE_TYPE("INFERENCEELEM");

	WRITE_NODE_FIELD(expr);
	WRITE_OID_FIELD(infercollid);
	WRITE_OID_FIELD(inferopclass);
}

static void fc__outTargetEntry(StringInfo fc_str, const TargetEntry *fc_node)
{
	WRITE_NODE_TYPE("TARGETENTRY");

	WRITE_NODE_FIELD(expr);
	WRITE_INT_FIELD(resno);
	WRITE_STRING_FIELD(resname);
	WRITE_UINT_FIELD(ressortgroupref);
	WRITE_OID_FIELD(resorigtbl);
	WRITE_INT_FIELD(resorigcol);
	WRITE_BOOL_FIELD(resjunk);
}

static void fc__outRangeTblRef(StringInfo fc_str, const RangeTblRef *fc_node)
{
	WRITE_NODE_TYPE("RANGETBLREF");

	WRITE_INT_FIELD(rtindex);
}

static void fc__outJoinExpr(StringInfo fc_str, const JoinExpr *fc_node)
{
	WRITE_NODE_TYPE("JOINEXPR");

	WRITE_ENUM_FIELD(jointype, JoinType);
	WRITE_BOOL_FIELD(isNatural);
	WRITE_NODE_FIELD(larg);
	WRITE_NODE_FIELD(rarg);
	WRITE_NODE_FIELD(usingClause);
	WRITE_NODE_FIELD(join_using_alias);
	WRITE_NODE_FIELD(quals);
	WRITE_NODE_FIELD(alias);
	WRITE_INT_FIELD(rtindex);
}

static void fc__outFromExpr(StringInfo fc_str, const FromExpr *fc_node)
{
	WRITE_NODE_TYPE("FROMEXPR");

	WRITE_NODE_FIELD(fromlist);
	WRITE_NODE_FIELD(quals);
}

static void fc__outOnConflictExpr(StringInfo fc_str, const OnConflictExpr *fc_node)
{
	WRITE_NODE_TYPE("ONCONFLICTEXPR");

	WRITE_ENUM_FIELD(action, OnConflictAction);
	WRITE_NODE_FIELD(arbiterElems);
	WRITE_NODE_FIELD(arbiterWhere);
	WRITE_OID_FIELD(constraint);
	WRITE_NODE_FIELD(onConflictSet);
	WRITE_NODE_FIELD(onConflictWhere);
	WRITE_INT_FIELD(exclRelIndex);
	WRITE_NODE_FIELD(exclRelTlist);
}

/*****************************************************************************
 *
 *	来自pathnodes.h的内容。
 *
 *****************************************************************************/

/*
 * 打印所有继承自Path的节点的基本内容
 *
 * 注意，我们不打印父节点，否则会陷入无限递归。
 * 不过，我们可以打印父节点的relids以便于识别。
 * 仅在pathtarget不是默认的rel时打印它。
 * 我们也不打印param_info的全部内容，因为它由
 * _outRelOptInfo打印；只打印所需的外部relids就已经足够且更清晰。
 */
static void fc__outPathInfo(StringInfo fc_str, const Path *fc_node)
{
	WRITE_ENUM_FIELD(pathtype, NodeTag);
	appendStringInfoString(fc_str, " :parent_relids ");
	outBitmapset(fc_str, fc_node->parent->relids);
	if (fc_node->pathtarget != fc_node->parent->reltarget)
		WRITE_NODE_FIELD(pathtarget);
	appendStringInfoString(fc_str, " :required_outer ");
	if (fc_node->param_info)
		outBitmapset(fc_str, fc_node->param_info->ppi_req_outer);
	else
		outBitmapset(fc_str, NULL);
	WRITE_BOOL_FIELD(parallel_aware);
	WRITE_BOOL_FIELD(parallel_safe);
	WRITE_INT_FIELD(parallel_workers);
	WRITE_FLOAT_FIELD(rows, "%.0f");
	WRITE_FLOAT_FIELD(startup_cost, "%.2f");
	WRITE_FLOAT_FIELD(total_cost, "%.2f");
	WRITE_NODE_FIELD(pathkeys);
}

/*
 * 打印所有继承自 JoinPath 的节点的基本信息
 */
static void fc__outJoinPathInfo(StringInfo fc_str, const JoinPath *fc_node)
{
	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_ENUM_FIELD(jointype, JoinType);
	WRITE_BOOL_FIELD(inner_unique);
	WRITE_NODE_FIELD(outerjoinpath);
	WRITE_NODE_FIELD(innerjoinpath);
	WRITE_NODE_FIELD(joinrestrictinfo);
}

static void fc__outPath(StringInfo fc_str, const Path *fc_node)
{
	WRITE_NODE_TYPE("PATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);
}

static void fc__outIndexPath(StringInfo fc_str, const IndexPath *fc_node)
{
	WRITE_NODE_TYPE("INDEXPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(indexinfo);
	WRITE_NODE_FIELD(indexclauses);
	WRITE_NODE_FIELD(indexorderbys);
	WRITE_NODE_FIELD(indexorderbycols);
	WRITE_ENUM_FIELD(indexscandir, ScanDirection);
	WRITE_FLOAT_FIELD(indextotalcost, "%.2f");
	WRITE_FLOAT_FIELD(indexselectivity, "%.4f");
}

static void fc__outBitmapHeapPath(StringInfo fc_str, const BitmapHeapPath *fc_node)
{
	WRITE_NODE_TYPE("BITMAPHEAPPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(bitmapqual);
}

static void fc__outBitmapAndPath(StringInfo fc_str, const BitmapAndPath *fc_node)
{
	WRITE_NODE_TYPE("BITMAPANDPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(bitmapquals);
	WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f");
}

static void fc__outBitmapOrPath(StringInfo fc_str, const BitmapOrPath *fc_node)
{
	WRITE_NODE_TYPE("BITMAPORPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(bitmapquals);
	WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f");
}

static void fc__outTidPath(StringInfo fc_str, const TidPath *fc_node)
{
	WRITE_NODE_TYPE("TIDPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(tidquals);
}

static void fc__outTidRangePath(StringInfo fc_str, const TidRangePath *fc_node)
{
	WRITE_NODE_TYPE("TIDRANGEPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(tidrangequals);
}

static void fc__outSubqueryScanPath(StringInfo fc_str, const SubqueryScanPath *fc_node)
{
	WRITE_NODE_TYPE("SUBQUERYSCANPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
}

static void fc__outForeignPath(StringInfo fc_str, const ForeignPath *fc_node)
{
	WRITE_NODE_TYPE("FOREIGNPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(fdw_outerpath);
	WRITE_NODE_FIELD(fdw_private);
}

static void fc__outCustomPath(StringInfo fc_str, const CustomPath *fc_node)
{
	WRITE_NODE_TYPE("CUSTOMPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_UINT_FIELD(flags);
	WRITE_NODE_FIELD(custom_paths);
	WRITE_NODE_FIELD(custom_private);
	appendStringInfoString(fc_str, " :methods ");
	outToken(fc_str, fc_node->methods->CustomName);
}

static void fc__outAppendPath(StringInfo fc_str, const AppendPath *fc_node)
{
	WRITE_NODE_TYPE("APPENDPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpaths);
	WRITE_INT_FIELD(first_partial_path);
	WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
}

static void fc__outMergeAppendPath(StringInfo fc_str, const MergeAppendPath *fc_node)
{
	WRITE_NODE_TYPE("MERGEAPPENDPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpaths);
	WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
}

static void fc__outGroupResultPath(StringInfo fc_str, const GroupResultPath *fc_node)
{
	WRITE_NODE_TYPE("GROUPRESULTPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(quals);
}

static void fc__outMaterialPath(StringInfo fc_str, const MaterialPath *fc_node)
{
	WRITE_NODE_TYPE("MATERIALPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
}

static void fc__outMemoizePath(StringInfo fc_str, const MemoizePath *fc_node)
{
	WRITE_NODE_TYPE("MEMOIZEPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_NODE_FIELD(hash_operators);
	WRITE_NODE_FIELD(param_exprs);
	WRITE_BOOL_FIELD(singlerow);
	WRITE_BOOL_FIELD(binary_mode);
	WRITE_FLOAT_FIELD(calls, "%.0f");
	WRITE_UINT_FIELD(est_entries);
}

static void fc__outUniquePath(StringInfo fc_str, const UniquePath *fc_node)
{
	WRITE_NODE_TYPE("UNIQUEPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_ENUM_FIELD(umethod, UniquePathMethod);
	WRITE_NODE_FIELD(in_operators);
	WRITE_NODE_FIELD(uniq_exprs);
}

static void fc__outGatherPath(StringInfo fc_str, const GatherPath *fc_node)
{
	WRITE_NODE_TYPE("GATHERPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_BOOL_FIELD(single_copy);
	WRITE_INT_FIELD(num_workers);
}

static void fc__outProjectionPath(StringInfo fc_str, const ProjectionPath *fc_node)
{
	WRITE_NODE_TYPE("PROJECTIONPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_BOOL_FIELD(dummypp);
}

static void fc__outProjectSetPath(StringInfo fc_str, const ProjectSetPath *fc_node)
{
	WRITE_NODE_TYPE("PROJECTSETPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
}

static void fc__outSortPathInfo(StringInfo fc_str, const SortPath *fc_node)
{
	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
}

static void fc__outSortPath(StringInfo fc_str, const SortPath *fc_node)
{
	WRITE_NODE_TYPE("SORTPATH");

	fc__outSortPathInfo(fc_str, fc_node);
}

static void fc__outIncrementalSortPath(StringInfo fc_str, const IncrementalSortPath *fc_node)
{
	WRITE_NODE_TYPE("INCREMENTALSORTPATH");

	fc__outSortPathInfo(fc_str, (const SortPath *) fc_node);

	WRITE_INT_FIELD(nPresortedCols);
}

static void fc__outGroupPath(StringInfo fc_str, const GroupPath *fc_node)
{
	WRITE_NODE_TYPE("GROUPPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_NODE_FIELD(groupClause);
	WRITE_NODE_FIELD(qual);
}

static void fc__outUpperUniquePath(StringInfo fc_str, const UpperUniquePath *fc_node)
{
	WRITE_NODE_TYPE("UPPERUNIQUEPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_INT_FIELD(numkeys);
}

static void fc__outAggPath(StringInfo fc_str, const AggPath *fc_node)
{
	WRITE_NODE_TYPE("AGGPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_ENUM_FIELD(aggstrategy, AggStrategy);
	WRITE_ENUM_FIELD(aggsplit, AggSplit);
	WRITE_FLOAT_FIELD(numGroups, "%.0f");
	WRITE_UINT64_FIELD(transitionSpace);
	WRITE_NODE_FIELD(groupClause);
	WRITE_NODE_FIELD(qual);
}

static void fc__outRollupData(StringInfo fc_str, const RollupData *fc_node)
{
	WRITE_NODE_TYPE("ROLLUP");

	WRITE_NODE_FIELD(groupClause);
	WRITE_NODE_FIELD(gsets);
	WRITE_NODE_FIELD(gsets_data);
	WRITE_FLOAT_FIELD(numGroups, "%.0f");
	WRITE_BOOL_FIELD(hashable);
	WRITE_BOOL_FIELD(is_hashed);
}

static void fc__outGroupingSetData(StringInfo fc_str, const GroupingSetData *fc_node)
{
	WRITE_NODE_TYPE("GSDATA");

	WRITE_NODE_FIELD(set);
	WRITE_FLOAT_FIELD(numGroups, "%.0f");
}

static void fc__outGroupingSetsPath(StringInfo fc_str, const GroupingSetsPath *fc_node)
{
	WRITE_NODE_TYPE("GROUPINGSETSPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_ENUM_FIELD(aggstrategy, AggStrategy);
	WRITE_NODE_FIELD(rollups);
	WRITE_NODE_FIELD(qual);
	WRITE_UINT64_FIELD(transitionSpace);
}

static void fc__outMinMaxAggPath(StringInfo fc_str, const MinMaxAggPath *fc_node)
{
	WRITE_NODE_TYPE("MINMAXAGGPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(mmaggregates);
	WRITE_NODE_FIELD(quals);
}

static void fc__outWindowAggPath(StringInfo fc_str, const WindowAggPath *fc_node)
{
	WRITE_NODE_TYPE("WINDOWAGGPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_NODE_FIELD(winclause);
	WRITE_NODE_FIELD(qual);
	WRITE_BOOL_FIELD(topwindow);
}

static void fc__outSetOpPath(StringInfo fc_str, const SetOpPath *fc_node)
{
	WRITE_NODE_TYPE("SETOPPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_ENUM_FIELD(cmd, SetOpCmd);
	WRITE_ENUM_FIELD(strategy, SetOpStrategy);
	WRITE_NODE_FIELD(distinctList);
	WRITE_INT_FIELD(flagColIdx);
	WRITE_INT_FIELD(firstFlag);
	WRITE_FLOAT_FIELD(numGroups, "%.0f");
}

static void fc__outRecursiveUnionPath(StringInfo fc_str, const RecursiveUnionPath *fc_node)
{
	WRITE_NODE_TYPE("RECURSIVEUNIONPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(leftpath);
	WRITE_NODE_FIELD(rightpath);
	WRITE_NODE_FIELD(distinctList);
	WRITE_INT_FIELD(wtParam);
	WRITE_FLOAT_FIELD(numGroups, "%.0f");
}

static void fc__outLockRowsPath(StringInfo fc_str, const LockRowsPath *fc_node)
{
	WRITE_NODE_TYPE("LOCKROWSPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_NODE_FIELD(rowMarks);
	WRITE_INT_FIELD(epqParam);
}

static void fc__outModifyTablePath(StringInfo fc_str, const ModifyTablePath *fc_node)
{
	WRITE_NODE_TYPE("MODIFYTABLEPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_ENUM_FIELD(operation, CmdType);
	WRITE_BOOL_FIELD(canSetTag);
	WRITE_UINT_FIELD(nominalRelation);
	WRITE_UINT_FIELD(rootRelation);
	WRITE_BOOL_FIELD(partColsUpdated);
	WRITE_NODE_FIELD(resultRelations);
	WRITE_NODE_FIELD(updateColnosLists);
	WRITE_NODE_FIELD(withCheckOptionLists);
	WRITE_NODE_FIELD(returningLists);
	WRITE_NODE_FIELD(rowMarks);
	WRITE_NODE_FIELD(onconflict);
	WRITE_INT_FIELD(epqParam);
	WRITE_NODE_FIELD(mergeActionLists);
}

static void fc__outLimitPath(StringInfo fc_str, const LimitPath *fc_node)
{
	WRITE_NODE_TYPE("LIMITPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_NODE_FIELD(limitOffset);
	WRITE_NODE_FIELD(limitCount);
	WRITE_ENUM_FIELD(limitOption, LimitOption);
}

static void fc__outGatherMergePath(StringInfo fc_str, const GatherMergePath *fc_node)
{
	WRITE_NODE_TYPE("GATHERMERGEPATH");

	fc__outPathInfo(fc_str, (const Path *) fc_node);

	WRITE_NODE_FIELD(subpath);
	WRITE_INT_FIELD(num_workers);
}

static void fc__outNestPath(StringInfo fc_str, const NestPath *fc_node)
{
	WRITE_NODE_TYPE("NESTPATH");

	fc__outJoinPathInfo(fc_str, (const JoinPath *) fc_node);
}

static void fc__outMergePath(StringInfo fc_str, const MergePath *fc_node)
{
	WRITE_NODE_TYPE("MERGEPATH");

	fc__outJoinPathInfo(fc_str, (const JoinPath *) fc_node);

	WRITE_NODE_FIELD(path_mergeclauses);
	WRITE_NODE_FIELD(outersortkeys);
	WRITE_NODE_FIELD(innersortkeys);
	WRITE_BOOL_FIELD(skip_mark_restore);
	WRITE_BOOL_FIELD(materialize_inner);
}

static void fc__outHashPath(StringInfo fc_str, const HashPath *fc_node)
{
	WRITE_NODE_TYPE("HASHPATH");

	fc__outJoinPathInfo(fc_str, (const JoinPath *) fc_node);

	WRITE_NODE_FIELD(path_hashclauses);
	WRITE_INT_FIELD(num_batches);
	WRITE_FLOAT_FIELD(inner_rows_total, "%.0f");
}

static void fc__outPlannerGlobal(StringInfo fc_str, const PlannerGlobal *fc_node)
{
	WRITE_NODE_TYPE("PLANNERGLOBAL");

	/* 注意：这不是一个完整的字段集 */
	WRITE_NODE_FIELD(subplans);
	WRITE_BITMAPSET_FIELD(rewindPlanIDs);
	WRITE_NODE_FIELD(finalrtable);
	WRITE_NODE_FIELD(finalrowmarks);
	WRITE_NODE_FIELD(resultRelations);
	WRITE_NODE_FIELD(appendRelations);
	WRITE_NODE_FIELD(relationOids);
	WRITE_NODE_FIELD(invalItems);
	WRITE_NODE_FIELD(paramExecTypes);
	WRITE_UINT_FIELD(lastPHId);
	WRITE_UINT_FIELD(lastRowMarkId);
	WRITE_INT_FIELD(lastPlanNodeId);
	WRITE_BOOL_FIELD(transientPlan);
	WRITE_BOOL_FIELD(dependsOnRole);
	WRITE_BOOL_FIELD(parallelModeOK);
	WRITE_BOOL_FIELD(parallelModeNeeded);
	WRITE_CHAR_FIELD(maxParallelHazard);
}

static void fc__outPlannerInfo(StringInfo fc_str, const PlannerInfo *fc_node)
{
	WRITE_NODE_TYPE("PLANNERINFO");

	/* 注意：这不是一个完整的字段集 */
	WRITE_NODE_FIELD(parse);
	WRITE_NODE_FIELD(glob);
	WRITE_UINT_FIELD(query_level);
	WRITE_NODE_FIELD(plan_params);
	WRITE_BITMAPSET_FIELD(outer_params);
	WRITE_BITMAPSET_FIELD(all_baserels);
	WRITE_BITMAPSET_FIELD(nullable_baserels);
	WRITE_NODE_FIELD(join_rel_list);
	WRITE_INT_FIELD(join_cur_level);
	WRITE_NODE_FIELD(init_plans);
	WRITE_NODE_FIELD(cte_plan_ids);
	WRITE_NODE_FIELD(multiexpr_params);
	WRITE_NODE_FIELD(eq_classes);
	WRITE_BOOL_FIELD(ec_merging_done);
	WRITE_NODE_FIELD(canon_pathkeys);
	WRITE_NODE_FIELD(left_join_clauses);
	WRITE_NODE_FIELD(right_join_clauses);
	WRITE_NODE_FIELD(full_join_clauses);
	WRITE_NODE_FIELD(join_info_list);
	WRITE_BITMAPSET_FIELD(all_result_relids);
	WRITE_BITMAPSET_FIELD(leaf_result_relids);
	WRITE_NODE_FIELD(append_rel_list);
	WRITE_NODE_FIELD(row_identity_vars);
	WRITE_NODE_FIELD(rowMarks);
	WRITE_NODE_FIELD(placeholder_list);
	WRITE_NODE_FIELD(fkey_list);
	WRITE_NODE_FIELD(query_pathkeys);
	WRITE_NODE_FIELD(group_pathkeys);
	WRITE_NODE_FIELD(window_pathkeys);
	WRITE_NODE_FIELD(distinct_pathkeys);
	WRITE_NODE_FIELD(sort_pathkeys);
	WRITE_NODE_FIELD(processed_tlist);
	WRITE_NODE_FIELD(update_colnos);
	WRITE_NODE_FIELD(minmax_aggs);
	WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
	WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
	WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
	WRITE_UINT_FIELD(qual_security_level);
	WRITE_BOOL_FIELD(hasJoinRTEs);
	WRITE_BOOL_FIELD(hasLateralRTEs);
	WRITE_BOOL_FIELD(hasHavingQual);
	WRITE_BOOL_FIELD(hasPseudoConstantQuals);
	WRITE_BOOL_FIELD(hasAlternativeSubPlans);
	WRITE_BOOL_FIELD(hasRecursion);
	WRITE_INT_FIELD(wt_param_id);
	WRITE_BITMAPSET_FIELD(curOuterRels);
	WRITE_NODE_FIELD(curOuterParams);
	WRITE_BOOL_FIELD(partColsUpdated);
}

static void fc__outRelOptInfo(StringInfo fc_str, const RelOptInfo *fc_node)
{
	WRITE_NODE_TYPE("RELOPTINFO");

	/* 注意：这不是一个完整的字段集 */
	WRITE_ENUM_FIELD(reloptkind, RelOptKind);
	WRITE_BITMAPSET_FIELD(relids);
	WRITE_FLOAT_FIELD(rows, "%.0f");
	WRITE_BOOL_FIELD(consider_startup);
	WRITE_BOOL_FIELD(consider_param_startup);
	WRITE_BOOL_FIELD(consider_parallel);
	WRITE_NODE_FIELD(reltarget);
	WRITE_NODE_FIELD(pathlist);
	WRITE_NODE_FIELD(ppilist);
	WRITE_NODE_FIELD(partial_pathlist);
	WRITE_NODE_FIELD(cheapest_startup_path);
	WRITE_NODE_FIELD(cheapest_total_path);
	WRITE_NODE_FIELD(cheapest_unique_path);
	WRITE_NODE_FIELD(cheapest_parameterized_paths);
	WRITE_BITMAPSET_FIELD(direct_lateral_relids);
	WRITE_BITMAPSET_FIELD(lateral_relids);
	WRITE_UINT_FIELD(relid);
	WRITE_OID_FIELD(reltablespace);
	WRITE_ENUM_FIELD(rtekind, RTEKind);
	WRITE_INT_FIELD(min_attr);
	WRITE_INT_FIELD(max_attr);
	WRITE_NODE_FIELD(lateral_vars);
	WRITE_BITMAPSET_FIELD(lateral_referencers);
	WRITE_NODE_FIELD(indexlist);
	WRITE_NODE_FIELD(statlist);
	WRITE_UINT_FIELD(pages);
	WRITE_FLOAT_FIELD(tuples, "%.0f");
	WRITE_FLOAT_FIELD(allvisfrac, "%.6f");
	WRITE_BITMAPSET_FIELD(eclass_indexes);
	WRITE_NODE_FIELD(subroot);
	WRITE_NODE_FIELD(subplan_params);
	WRITE_INT_FIELD(rel_parallel_workers);
	WRITE_UINT_FIELD(amflags);
	WRITE_OID_FIELD(serverid);
	WRITE_OID_FIELD(userid);
	WRITE_BOOL_FIELD(useridiscurrent);
	/* 我们不试图打印 fdwroutine 或 fdw_private */
	/* 不能打印 unique_for_rels/non_unique_for_rels; BMSes 不是节点 */
	WRITE_NODE_FIELD(baserestrictinfo);
	WRITE_UINT_FIELD(baserestrict_min_security);
	WRITE_NODE_FIELD(joininfo);
	WRITE_BOOL_FIELD(has_eclass_joins);
	WRITE_BOOL_FIELD(consider_partitionwise_join);
	WRITE_BITMAPSET_FIELD(top_parent_relids);
	WRITE_BOOL_FIELD(partbounds_merged);
	WRITE_BITMAPSET_FIELD(live_parts);
	WRITE_BITMAPSET_FIELD(all_partrels);
}

static void fc__outIndexOptInfo(StringInfo fc_str, const IndexOptInfo *fc_node)
{
	WRITE_NODE_TYPE("INDEXOPTINFO");

	/* 注意：这不是一个完整的字段集 */
	WRITE_OID_FIELD(indexoid);
	/* 不要打印 rel 字段，否则会无限递归 */
	WRITE_UINT_FIELD(pages);
	WRITE_FLOAT_FIELD(tuples, "%.0f");
	WRITE_INT_FIELD(tree_height);
	WRITE_INT_FIELD(ncolumns);
	/* 数组字段不值得打印 */
	WRITE_OID_FIELD(relam);
	/* indexprs 是多余的，因为我们打印 indextlist */
	WRITE_NODE_FIELD(indpred);
	WRITE_NODE_FIELD(indextlist);
	WRITE_NODE_FIELD(indrestrictinfo);
	WRITE_BOOL_FIELD(predOK);
	WRITE_BOOL_FIELD(unique);
	WRITE_BOOL_FIELD(immediate);
	WRITE_BOOL_FIELD(hypothetical);
	/* 我们不考虑从索引 AM 的 API 结构中复制的字段 */
}

static void fc__outForeignKeyOptInfo(StringInfo fc_str, const ForeignKeyOptInfo *fc_node)
{
	int			fc_i;

	WRITE_NODE_TYPE("FOREIGNKEYOPTINFO");

	WRITE_UINT_FIELD(con_relid);
	WRITE_UINT_FIELD(ref_relid);
	WRITE_INT_FIELD(nkeys);
	WRITE_ATTRNUMBER_ARRAY(conkey, fc_node->nkeys);
	WRITE_ATTRNUMBER_ARRAY(confkey, fc_node->nkeys);
	WRITE_OID_ARRAY(conpfeqop, fc_node->nkeys);
	WRITE_INT_FIELD(nmatched_ec);
	WRITE_INT_FIELD(nconst_ec);
	WRITE_INT_FIELD(nmatched_rcols);
	WRITE_INT_FIELD(nmatched_ri);
	/* 为了简洁，只打印每列的匹配数： */
	appendStringInfoString(fc_str, " :eclass");
	for (fc_i = 0; fc_i < fc_node->nkeys; fc_i++)
		appendStringInfo(fc_str, " %d", (fc_node->eclass[fc_i] != NULL));
	appendStringInfoString(fc_str, " :rinfos");
	for (fc_i = 0; fc_i < fc_node->nkeys; fc_i++)
		appendStringInfo(fc_str, " %d", list_length(fc_node->rinfos[fc_i]));
}

static void fc__outStatisticExtInfo(StringInfo fc_str, const StatisticExtInfo *fc_node)
{
	WRITE_NODE_TYPE("STATISTICEXTINFO");

	/* 注意：这不是一个完整的字段集 */
	WRITE_OID_FIELD(statOid);
	/* 不要写 rel，会导致计划树转储中的无限递归 */
	WRITE_CHAR_FIELD(kind);
	WRITE_BITMAPSET_FIELD(keys);
}

static void fc__outEquivalenceClass(StringInfo fc_str, const EquivalenceClass *fc_node)
{
	/*
	 * 为了简化阅读，我们只追溯到最上层合并的 EC 并
	 * 打印它，而不费心单独显示合并的节点。
	 */
	while (fc_node->ec_merged)
		fc_node = fc_node->ec_merged;

	WRITE_NODE_TYPE("EQUIVALENCECLASS");

	WRITE_NODE_FIELD(ec_opfamilies);
	WRITE_OID_FIELD(ec_collation);
	WRITE_NODE_FIELD(ec_members);
	WRITE_NODE_FIELD(ec_sources);
	WRITE_NODE_FIELD(ec_derives);
	WRITE_BITMAPSET_FIELD(ec_relids);
	WRITE_BOOL_FIELD(ec_has_const);
	WRITE_BOOL_FIELD(ec_has_volatile);
	WRITE_BOOL_FIELD(ec_below_outer_join);
	WRITE_BOOL_FIELD(ec_broken);
	WRITE_UINT_FIELD(ec_sortref);
	WRITE_UINT_FIELD(ec_min_security);
	WRITE_UINT_FIELD(ec_max_security);
}

static void fc__outEquivalenceMember(StringInfo fc_str, const EquivalenceMember *fc_node)
{
	WRITE_NODE_TYPE("EQUIVALENCEMEMBER");

	WRITE_NODE_FIELD(em_expr);
	WRITE_BITMAPSET_FIELD(em_relids);
	WRITE_BITMAPSET_FIELD(em_nullable_relids);
	WRITE_BOOL_FIELD(em_is_const);
	WRITE_BOOL_FIELD(em_is_child);
	WRITE_OID_FIELD(em_datatype);
}

static void fc__outPathKey(StringInfo fc_str, const PathKey *fc_node)
{
	WRITE_NODE_TYPE("PATHKEY");

	WRITE_NODE_FIELD(pk_eclass);
	WRITE_OID_FIELD(pk_opfamily);
	WRITE_INT_FIELD(pk_strategy);
	WRITE_BOOL_FIELD(pk_nulls_first);
}

static void fc__outPathTarget(StringInfo fc_str, const PathTarget *fc_node)
{
	WRITE_NODE_TYPE("PATHTARGET");

	WRITE_NODE_FIELD(exprs);
	WRITE_INDEX_ARRAY(sortgrouprefs, list_length(fc_node->exprs));
	WRITE_FLOAT_FIELD(cost.startup, "%.2f");
	WRITE_FLOAT_FIELD(cost.per_tuple, "%.2f");
	WRITE_INT_FIELD(width);
	WRITE_ENUM_FIELD(has_volatile_expr, VolatileFunctionStatus);
}

static void fc__outParamPathInfo(StringInfo fc_str, const ParamPathInfo *fc_node)
{
	WRITE_NODE_TYPE("PARAMPATHINFO");

	WRITE_BITMAPSET_FIELD(ppi_req_outer);
	WRITE_FLOAT_FIELD(ppi_rows, "%.0f");
	WRITE_NODE_FIELD(ppi_clauses);
}

static void fc__outRestrictInfo(StringInfo fc_str, const RestrictInfo *fc_node)
{
	WRITE_NODE_TYPE("RESTRICTINFO");

	/* 注意：这不是一个完整的字段集 */
	WRITE_NODE_FIELD(clause);
	WRITE_BOOL_FIELD(is_pushed_down);
	WRITE_BOOL_FIELD(outerjoin_delayed);
	WRITE_BOOL_FIELD(can_join);
	WRITE_BOOL_FIELD(pseudoconstant);
	WRITE_BOOL_FIELD(leakproof);
	WRITE_ENUM_FIELD(has_volatile, VolatileFunctionStatus);
	WRITE_UINT_FIELD(security_level);
	WRITE_BITMAPSET_FIELD(clause_relids);
	WRITE_BITMAPSET_FIELD(required_relids);
	WRITE_BITMAPSET_FIELD(outer_relids);
	WRITE_BITMAPSET_FIELD(nullable_relids);
	WRITE_BITMAPSET_FIELD(left_relids);
	WRITE_BITMAPSET_FIELD(right_relids);
	WRITE_NODE_FIELD(orclause);
	/* 不要写 parent_ec，会导致计划树转储中的无限递归 */
	WRITE_FLOAT_FIELD(norm_selec, "%.4f");
	WRITE_FLOAT_FIELD(outer_selec, "%.4f");
	WRITE_NODE_FIELD(mergeopfamilies);
	/* 不要写 left_ec，会导致计划树转储中的无限递归 */
	/* 不要写 right_ec，会导致计划树转储中的无限递归 */
	WRITE_NODE_FIELD(left_em);
	WRITE_NODE_FIELD(right_em);
	WRITE_BOOL_FIELD(outer_is_left);
	WRITE_OID_FIELD(hashjoinoperator);
	WRITE_OID_FIELD(left_hasheqoperator);
	WRITE_OID_FIELD(right_hasheqoperator);
}

static void fc__outIndexClause(StringInfo fc_str, const IndexClause *fc_node)
{
	WRITE_NODE_TYPE("INDEXCLAUSE");

	WRITE_NODE_FIELD(rinfo);
	WRITE_NODE_FIELD(indexquals);
	WRITE_BOOL_FIELD(lossy);
	WRITE_INT_FIELD(indexcol);
	WRITE_NODE_FIELD(indexcols);
}

static void fc__outPlaceHolderVar(StringInfo fc_str, const PlaceHolderVar *fc_node)
{
	WRITE_NODE_TYPE("PLACEHOLDERVAR");

	WRITE_NODE_FIELD(phexpr);
	WRITE_BITMAPSET_FIELD(phrels);
	WRITE_UINT_FIELD(phid);
	WRITE_UINT_FIELD(phlevelsup);
}

static void fc__outSpecialJoinInfo(StringInfo fc_str, const SpecialJoinInfo *fc_node)
{
	WRITE_NODE_TYPE("SPECIALJOININFO");

	WRITE_BITMAPSET_FIELD(min_lefthand);
	WRITE_BITMAPSET_FIELD(min_righthand);
	WRITE_BITMAPSET_FIELD(syn_lefthand);
	WRITE_BITMAPSET_FIELD(syn_righthand);
	WRITE_ENUM_FIELD(jointype, JoinType);
	WRITE_BOOL_FIELD(lhs_strict);
	WRITE_BOOL_FIELD(delay_upper_joins);
	WRITE_BOOL_FIELD(semi_can_btree);
	WRITE_BOOL_FIELD(semi_can_hash);
	WRITE_NODE_FIELD(semi_operators);
	WRITE_NODE_FIELD(semi_rhs_exprs);
}

static void fc__outAppendRelInfo(StringInfo fc_str, const AppendRelInfo *fc_node)
{
	WRITE_NODE_TYPE("APPENDRELINFO");

	WRITE_UINT_FIELD(parent_relid);
	WRITE_UINT_FIELD(child_relid);
	WRITE_OID_FIELD(parent_reltype);
	WRITE_OID_FIELD(child_reltype);
	WRITE_NODE_FIELD(translated_vars);
	WRITE_INT_FIELD(num_child_cols);
	WRITE_ATTRNUMBER_ARRAY(parent_colnos, fc_node->num_child_cols);
	WRITE_OID_FIELD(parent_reloid);
}

static void fc__outRowIdentityVarInfo(StringInfo fc_str, const RowIdentityVarInfo *fc_node)
{
	WRITE_NODE_TYPE("ROWIDENTITYVARINFO");

	WRITE_NODE_FIELD(rowidvar);
	WRITE_INT_FIELD(rowidwidth);
	WRITE_STRING_FIELD(rowidname);
	WRITE_BITMAPSET_FIELD(rowidrels);
}

static void fc__outPlaceHolderInfo(StringInfo fc_str, const PlaceHolderInfo *fc_node)
{
	WRITE_NODE_TYPE("PLACEHOLDERINFO");

	WRITE_UINT_FIELD(phid);
	WRITE_NODE_FIELD(ph_var);
	WRITE_BITMAPSET_FIELD(ph_eval_at);
	WRITE_BITMAPSET_FIELD(ph_lateral);
	WRITE_BITMAPSET_FIELD(ph_needed);
	WRITE_INT_FIELD(ph_width);
}

static void fc__outMinMaxAggInfo(StringInfo fc_str, const MinMaxAggInfo *fc_node)
{
	WRITE_NODE_TYPE("MINMAXAGGINFO");

	WRITE_OID_FIELD(aggfnoid);
	WRITE_OID_FIELD(aggsortop);
	WRITE_NODE_FIELD(target);
	/* 我们故意省略 subroot --- 太大，且不够有趣 */
	WRITE_NODE_FIELD(path);
	WRITE_FLOAT_FIELD(pathcost, "%.2f");
	WRITE_NODE_FIELD(param);
}

static void fc__outPlannerParamItem(StringInfo fc_str, const PlannerParamItem *fc_node)
{
	WRITE_NODE_TYPE("PLANNERPARAMITEM");

	WRITE_NODE_FIELD(item);
	WRITE_INT_FIELD(paramId);
}

/*****************************************************************************
 *
 *	来自 extensible.h 的内容
 *
 *****************************************************************************/

static void fc__outExtensibleNode(StringInfo fc_str, const ExtensibleNode *fc_node)
{
	const ExtensibleNodeMethods *fc_methods;

	fc_methods = GetExtensibleNodeMethods(fc_node->extnodename, false);

	WRITE_NODE_TYPE("EXTENSIBLENODE");

	WRITE_STRING_FIELD(extnodename);

	/* 序列化私有字段 */
	fc_methods->nodeOut(fc_str, fc_node);
}

/*****************************************************************************
 *
 *	来自 parsenodes.h 的内容.
 *
 *****************************************************************************/

/*
 * 打印所有继承自 CreateStmt 的节点的基本信息
 */
static void fc__outCreateStmtInfo(StringInfo fc_str, const CreateStmt *fc_node)
{
	WRITE_NODE_FIELD(relation);
	WRITE_NODE_FIELD(tableElts);
	WRITE_NODE_FIELD(inhRelations);
	WRITE_NODE_FIELD(partspec);
	WRITE_NODE_FIELD(partbound);
	WRITE_NODE_FIELD(ofTypename);
	WRITE_NODE_FIELD(constraints);
	WRITE_NODE_FIELD(options);
	WRITE_ENUM_FIELD(oncommit, OnCommitAction);
	WRITE_STRING_FIELD(tablespacename);
	WRITE_STRING_FIELD(accessMethod);
	WRITE_BOOL_FIELD(if_not_exists);
}

static void fc__outCreateStmt(StringInfo fc_str, const CreateStmt *fc_node)
{
	WRITE_NODE_TYPE("CREATESTMT");

	fc__outCreateStmtInfo(fc_str, (const CreateStmt *) fc_node);
}

static void fc__outCreateForeignTableStmt(StringInfo fc_str, const CreateForeignTableStmt *fc_node)
{
	WRITE_NODE_TYPE("CREATEFOREIGNTABLESTMT");

	fc__outCreateStmtInfo(fc_str, (const CreateStmt *) fc_node);

	WRITE_STRING_FIELD(servername);
	WRITE_NODE_FIELD(options);
}

static void fc__outImportForeignSchemaStmt(StringInfo fc_str, const ImportForeignSchemaStmt *fc_node)
{
	WRITE_NODE_TYPE("IMPORTFOREIGNSCHEMASTMT");

	WRITE_STRING_FIELD(server_name);
	WRITE_STRING_FIELD(remote_schema);
	WRITE_STRING_FIELD(local_schema);
	WRITE_ENUM_FIELD(list_type, ImportForeignSchemaType);
	WRITE_NODE_FIELD(table_list);
	WRITE_NODE_FIELD(options);
}

static void fc__outIndexStmt(StringInfo fc_str, const IndexStmt *fc_node)
{
	WRITE_NODE_TYPE("INDEXSTMT");

	WRITE_STRING_FIELD(idxname);
	WRITE_NODE_FIELD(relation);
	WRITE_STRING_FIELD(accessMethod);
	WRITE_STRING_FIELD(tableSpace);
	WRITE_NODE_FIELD(indexParams);
	WRITE_NODE_FIELD(indexIncludingParams);
	WRITE_NODE_FIELD(options);
	WRITE_NODE_FIELD(whereClause);
	WRITE_NODE_FIELD(excludeOpNames);
	WRITE_STRING_FIELD(idxcomment);
	WRITE_OID_FIELD(indexOid);
	WRITE_OID_FIELD(oldNode);
	WRITE_UINT_FIELD(oldCreateSubid);
	WRITE_UINT_FIELD(oldFirstRelfilenodeSubid);
	WRITE_BOOL_FIELD(unique);
	WRITE_BOOL_FIELD(nulls_not_distinct);
	WRITE_BOOL_FIELD(primary);
	WRITE_BOOL_FIELD(isconstraint);
	WRITE_BOOL_FIELD(deferrable);
	WRITE_BOOL_FIELD(initdeferred);
	WRITE_BOOL_FIELD(transformed);
	WRITE_BOOL_FIELD(concurrent);
	WRITE_BOOL_FIELD(if_not_exists);
	WRITE_BOOL_FIELD(reset_default_tblspc);
}

static void fc__outCreateStatsStmt(StringInfo fc_str, const CreateStatsStmt *fc_node)
{
	WRITE_NODE_TYPE("CREATESTATSSTMT");

	WRITE_NODE_FIELD(defnames);
	WRITE_NODE_FIELD(stat_types);
	WRITE_NODE_FIELD(exprs);
	WRITE_NODE_FIELD(relations);
	WRITE_STRING_FIELD(stxcomment);
	WRITE_BOOL_FIELD(transformed);
	WRITE_BOOL_FIELD(if_not_exists);
}

static void fc__outAlterStatsStmt(StringInfo fc_str, const AlterStatsStmt *fc_node)
{
	WRITE_NODE_TYPE("ALTERSTATSSTMT");

	WRITE_NODE_FIELD(defnames);
	WRITE_INT_FIELD(stxstattarget);
	WRITE_BOOL_FIELD(missing_ok);
}

static void fc__outNotifyStmt(StringInfo fc_str, const NotifyStmt *fc_node)
{
	WRITE_NODE_TYPE("NOTIFYSTMT");

	WRITE_STRING_FIELD(conditionname);
	WRITE_STRING_FIELD(payload);
}

static void fc__outDeclareCursorStmt(StringInfo fc_str, const DeclareCursorStmt *fc_node)
{
	WRITE_NODE_TYPE("DECLARECURSORSTMT");

	WRITE_STRING_FIELD(portalname);
	WRITE_INT_FIELD(options);
	WRITE_NODE_FIELD(query);
}

static void fc__outSelectStmt(StringInfo fc_str, const SelectStmt *fc_node)
{
	WRITE_NODE_TYPE("SELECT");

	WRITE_NODE_FIELD(distinctClause);
	WRITE_NODE_FIELD(intoClause);
	WRITE_NODE_FIELD(targetList);
	WRITE_NODE_FIELD(fromClause);
	WRITE_NODE_FIELD(whereClause);
	WRITE_NODE_FIELD(groupClause);
	WRITE_BOOL_FIELD(groupDistinct);
	WRITE_NODE_FIELD(havingClause);
	WRITE_NODE_FIELD(windowClause);
	WRITE_NODE_FIELD(valuesLists);
	WRITE_NODE_FIELD(sortClause);
	WRITE_NODE_FIELD(limitOffset);
	WRITE_NODE_FIELD(limitCount);
	WRITE_ENUM_FIELD(limitOption, LimitOption);
	WRITE_NODE_FIELD(lockingClause);
	WRITE_NODE_FIELD(withClause);
	WRITE_ENUM_FIELD(op, SetOperation);
	WRITE_BOOL_FIELD(all);
	WRITE_NODE_FIELD(larg);
	WRITE_NODE_FIELD(rarg);
}

static void fc__outReturnStmt(StringInfo fc_str, const ReturnStmt *fc_node)
{
	WRITE_NODE_TYPE("RETURN");

	WRITE_NODE_FIELD(returnval);
}

static void fc__outPLAssignStmt(StringInfo fc_str, const PLAssignStmt *fc_node)
{
	WRITE_NODE_TYPE("PLASSIGN");

	WRITE_STRING_FIELD(name);
	WRITE_NODE_FIELD(indirection);
	WRITE_INT_FIELD(nnames);
	WRITE_NODE_FIELD(val);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outFuncCall(StringInfo fc_str, const FuncCall *fc_node)
{
	WRITE_NODE_TYPE("FUNCCALL");

	WRITE_NODE_FIELD(funcname);
	WRITE_NODE_FIELD(args);
	WRITE_NODE_FIELD(agg_order);
	WRITE_NODE_FIELD(agg_filter);
	WRITE_NODE_FIELD(over);
	WRITE_BOOL_FIELD(agg_within_group);
	WRITE_BOOL_FIELD(agg_star);
	WRITE_BOOL_FIELD(agg_distinct);
	WRITE_BOOL_FIELD(func_variadic);
	WRITE_ENUM_FIELD(funcformat, CoercionForm);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outDefElem(StringInfo fc_str, const DefElem *fc_node)
{
	WRITE_NODE_TYPE("DEFELEM");

	WRITE_STRING_FIELD(defnamespace);
	WRITE_STRING_FIELD(defname);
	WRITE_NODE_FIELD(arg);
	WRITE_ENUM_FIELD(defaction, DefElemAction);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outTableLikeClause(StringInfo fc_str, const TableLikeClause *fc_node)
{
	WRITE_NODE_TYPE("TABLELIKECLAUSE");

	WRITE_NODE_FIELD(relation);
	WRITE_UINT_FIELD(options);
	WRITE_OID_FIELD(relationOid);
}

static void fc__outLockingClause(StringInfo fc_str, const LockingClause *fc_node)
{
	WRITE_NODE_TYPE("LOCKINGCLAUSE");

	WRITE_NODE_FIELD(lockedRels);
	WRITE_ENUM_FIELD(strength, LockClauseStrength);
	WRITE_ENUM_FIELD(waitPolicy, LockWaitPolicy);
}

static void fc__outXmlSerialize(StringInfo fc_str, const XmlSerialize *fc_node)
{
	WRITE_NODE_TYPE("XMLSERIALIZE");

	WRITE_ENUM_FIELD(xmloption, XmlOptionType);
	WRITE_NODE_FIELD(expr);
	WRITE_NODE_FIELD(typeName);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outTriggerTransition(StringInfo fc_str, const TriggerTransition *fc_node)
{
	WRITE_NODE_TYPE("TRIGGERTRANSITION");

	WRITE_STRING_FIELD(name);
	WRITE_BOOL_FIELD(isNew);
	WRITE_BOOL_FIELD(isTable);
}

static void fc__outColumnDef(StringInfo fc_str, const ColumnDef *fc_node)
{
	WRITE_NODE_TYPE("COLUMNDEF");

	WRITE_STRING_FIELD(colname);
	WRITE_NODE_FIELD(typeName);
	WRITE_STRING_FIELD(compression);
	WRITE_INT_FIELD(inhcount);
	WRITE_BOOL_FIELD(is_local);
	WRITE_BOOL_FIELD(is_not_null);
	WRITE_BOOL_FIELD(is_from_type);
	WRITE_CHAR_FIELD(storage);
	WRITE_NODE_FIELD(raw_default);
	WRITE_NODE_FIELD(cooked_default);
	WRITE_CHAR_FIELD(identity);
	WRITE_NODE_FIELD(identitySequence);
	WRITE_CHAR_FIELD(generated);
	WRITE_NODE_FIELD(collClause);
	WRITE_OID_FIELD(collOid);
	WRITE_NODE_FIELD(constraints);
	WRITE_NODE_FIELD(fdwoptions);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outTypeName(StringInfo fc_str, const TypeName *fc_node)
{
	WRITE_NODE_TYPE("TYPENAME");

	WRITE_NODE_FIELD(names);
	WRITE_OID_FIELD(typeOid);
	WRITE_BOOL_FIELD(setof);
	WRITE_BOOL_FIELD(pct_type);
	WRITE_NODE_FIELD(typmods);
	WRITE_INT_FIELD(typemod);
	WRITE_NODE_FIELD(arrayBounds);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outTypeCast(StringInfo fc_str, const TypeCast *fc_node)
{
	WRITE_NODE_TYPE("TYPECAST");

	WRITE_NODE_FIELD(arg);
	WRITE_NODE_FIELD(typeName);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCollateClause(StringInfo fc_str, const CollateClause *fc_node)
{
	WRITE_NODE_TYPE("COLLATECLAUSE");

	WRITE_NODE_FIELD(arg);
	WRITE_NODE_FIELD(collname);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outIndexElem(StringInfo fc_str, const IndexElem *fc_node)
{
	WRITE_NODE_TYPE("INDEXELEM");

	WRITE_STRING_FIELD(name);
	WRITE_NODE_FIELD(expr);
	WRITE_STRING_FIELD(indexcolname);
	WRITE_NODE_FIELD(collation);
	WRITE_NODE_FIELD(opclass);
	WRITE_NODE_FIELD(opclassopts);
	WRITE_ENUM_FIELD(ordering, SortByDir);
	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
}

static void fc__outStatsElem(StringInfo fc_str, const StatsElem *fc_node)
{
	WRITE_NODE_TYPE("STATSELEM");

	WRITE_STRING_FIELD(name);
	WRITE_NODE_FIELD(expr);
}

static void fc__outQuery(StringInfo fc_str, const Query *fc_node)
{
	WRITE_NODE_TYPE("QUERY");

	WRITE_ENUM_FIELD(commandType, CmdType);
	WRITE_ENUM_FIELD(querySource, QuerySource);
	/* 我们故意不打印 queryId 字段 */
	WRITE_BOOL_FIELD(canSetTag);

	/*
	 * 解决缺少大量 utility-statement 节点类型的 outfuncs 例程的黑客方法。
	 * （我们实际需要的唯一一个是 NotifyStmt 以支持规则。）
	 * 总有一天我们应该支持它们所有，但目前这样做可以避免
	 * 在 debug_print_parse 开启时出现大量警告。
	 */
	if (fc_node->utilityStmt)
	{
		switch (nodeTag(fc_node->utilityStmt))
		{
			case T_CreateStmt:
			case T_IndexStmt:
			case T_NotifyStmt:
			case T_DeclareCursorStmt:
				WRITE_NODE_FIELD(utilityStmt);
				break;
			default:
				appendStringInfoString(fc_str, " :utilityStmt ?");
				break;
		}
	}
	else
		appendStringInfoString(fc_str, " :utilityStmt <>");

	WRITE_INT_FIELD(resultRelation);
	WRITE_BOOL_FIELD(hasAggs);
	WRITE_BOOL_FIELD(hasWindowFuncs);
	WRITE_BOOL_FIELD(hasTargetSRFs);
	WRITE_BOOL_FIELD(hasSubLinks);
	WRITE_BOOL_FIELD(hasDistinctOn);
	WRITE_BOOL_FIELD(hasRecursive);
	WRITE_BOOL_FIELD(hasModifyingCTE);
	WRITE_BOOL_FIELD(hasForUpdate);
	WRITE_BOOL_FIELD(hasRowSecurity);
	WRITE_BOOL_FIELD(isReturn);
	WRITE_NODE_FIELD(cteList);
	WRITE_NODE_FIELD(rtable);
	WRITE_NODE_FIELD(jointree);
	WRITE_NODE_FIELD(targetList);
	WRITE_ENUM_FIELD(override, OverridingKind);
	WRITE_NODE_FIELD(onConflict);
	WRITE_NODE_FIELD(returningList);
	WRITE_NODE_FIELD(groupClause);
	WRITE_BOOL_FIELD(groupDistinct);
	WRITE_NODE_FIELD(groupingSets);
	WRITE_NODE_FIELD(havingQual);
	WRITE_NODE_FIELD(windowClause);
	WRITE_NODE_FIELD(distinctClause);
	WRITE_NODE_FIELD(sortClause);
	WRITE_NODE_FIELD(limitOffset);
	WRITE_NODE_FIELD(limitCount);
	WRITE_ENUM_FIELD(limitOption, LimitOption);
	WRITE_NODE_FIELD(rowMarks);
	WRITE_NODE_FIELD(setOperations);
	WRITE_NODE_FIELD(constraintDeps);
	WRITE_NODE_FIELD(withCheckOptions);
	WRITE_NODE_FIELD(mergeActionList);
	WRITE_BOOL_FIELD(mergeUseOuterJoin);
	WRITE_LOCATION_FIELD(stmt_location);
	WRITE_INT_FIELD(stmt_len);
}

static void fc__outWithCheckOption(StringInfo fc_str, const WithCheckOption *fc_node)
{
	WRITE_NODE_TYPE("WITHCHECKOPTION");

	WRITE_ENUM_FIELD(kind, WCOKind);
	WRITE_STRING_FIELD(relname);
	WRITE_STRING_FIELD(polname);
	WRITE_NODE_FIELD(qual);
	WRITE_BOOL_FIELD(cascaded);
}

static void fc__outSortGroupClause(StringInfo fc_str, const SortGroupClause *fc_node)
{
	WRITE_NODE_TYPE("SORTGROUPCLAUSE");

	WRITE_UINT_FIELD(tleSortGroupRef);
	WRITE_OID_FIELD(eqop);
	WRITE_OID_FIELD(sortop);
	WRITE_BOOL_FIELD(nulls_first);
	WRITE_BOOL_FIELD(hashable);
}

static void fc__outGroupingSet(StringInfo fc_str, const GroupingSet *fc_node)
{
	WRITE_NODE_TYPE("GROUPINGSET");

	WRITE_ENUM_FIELD(kind, GroupingSetKind);
	WRITE_NODE_FIELD(content);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outWindowClause(StringInfo fc_str, const WindowClause *fc_node)
{
	WRITE_NODE_TYPE("WINDOWCLAUSE");

	WRITE_STRING_FIELD(name);
	WRITE_STRING_FIELD(refname);
	WRITE_NODE_FIELD(partitionClause);
	WRITE_NODE_FIELD(orderClause);
	WRITE_INT_FIELD(frameOptions);
	WRITE_NODE_FIELD(startOffset);
	WRITE_NODE_FIELD(endOffset);
	WRITE_NODE_FIELD(runCondition);
	WRITE_OID_FIELD(startInRangeFunc);
	WRITE_OID_FIELD(endInRangeFunc);
	WRITE_OID_FIELD(inRangeColl);
	WRITE_BOOL_FIELD(inRangeAsc);
	WRITE_BOOL_FIELD(inRangeNullsFirst);
	WRITE_UINT_FIELD(winref);
	WRITE_BOOL_FIELD(copiedOrder);
}

static void fc__outRowMarkClause(StringInfo fc_str, const RowMarkClause *fc_node)
{
	WRITE_NODE_TYPE("ROWMARKCLAUSE");

	WRITE_UINT_FIELD(rti);
	WRITE_ENUM_FIELD(strength, LockClauseStrength);
	WRITE_ENUM_FIELD(waitPolicy, LockWaitPolicy);
	WRITE_BOOL_FIELD(pushedDown);
}

static void fc__outWithClause(StringInfo fc_str, const WithClause *fc_node)
{
	WRITE_NODE_TYPE("WITHCLAUSE");

	WRITE_NODE_FIELD(ctes);
	WRITE_BOOL_FIELD(recursive);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCTESearchClause(StringInfo fc_str, const CTESearchClause *fc_node)
{
	WRITE_NODE_TYPE("CTESEARCHCLAUSE");

	WRITE_NODE_FIELD(search_col_list);
	WRITE_BOOL_FIELD(search_breadth_first);
	WRITE_STRING_FIELD(search_seq_column);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outCTECycleClause(StringInfo fc_str, const CTECycleClause *fc_node)
{
	WRITE_NODE_TYPE("CTECYCLECLAUSE");

	WRITE_NODE_FIELD(cycle_col_list);
	WRITE_STRING_FIELD(cycle_mark_column);
	WRITE_NODE_FIELD(cycle_mark_value);
	WRITE_NODE_FIELD(cycle_mark_default);
	WRITE_STRING_FIELD(cycle_path_column);
	WRITE_LOCATION_FIELD(location);
	WRITE_OID_FIELD(cycle_mark_type);
	WRITE_INT_FIELD(cycle_mark_typmod);
	WRITE_OID_FIELD(cycle_mark_collation);
	WRITE_OID_FIELD(cycle_mark_neop);
}

static void fc__outCommonTableExpr(StringInfo fc_str, const CommonTableExpr *fc_node)
{
	WRITE_NODE_TYPE("COMMONTABLEEXPR");

	WRITE_STRING_FIELD(ctename);
	WRITE_NODE_FIELD(aliascolnames);
	WRITE_ENUM_FIELD(ctematerialized, CTEMaterialize);
	WRITE_NODE_FIELD(ctequery);
	WRITE_NODE_FIELD(search_clause);
	WRITE_NODE_FIELD(cycle_clause);
	WRITE_LOCATION_FIELD(location);
	WRITE_BOOL_FIELD(cterecursive);
	WRITE_INT_FIELD(cterefcount);
	WRITE_NODE_FIELD(ctecolnames);
	WRITE_NODE_FIELD(ctecoltypes);
	WRITE_NODE_FIELD(ctecoltypmods);
	WRITE_NODE_FIELD(ctecolcollations);
}

static void fc__outMergeWhenClause(StringInfo fc_str, const MergeWhenClause *fc_node)
{
	WRITE_NODE_TYPE("MERGEWHENCLAUSE");

	WRITE_BOOL_FIELD(matched);
	WRITE_ENUM_FIELD(commandType, CmdType);
	WRITE_ENUM_FIELD(override, OverridingKind);
	WRITE_NODE_FIELD(condition);
	WRITE_NODE_FIELD(targetList);
	WRITE_NODE_FIELD(values);
}

static void fc__outMergeAction(StringInfo fc_str, const MergeAction *fc_node)
{
	WRITE_NODE_TYPE("MERGEACTION");

	WRITE_BOOL_FIELD(matched);
	WRITE_ENUM_FIELD(commandType, CmdType);
	WRITE_ENUM_FIELD(override, OverridingKind);
	WRITE_NODE_FIELD(qual);
	WRITE_NODE_FIELD(targetList);
	WRITE_NODE_FIELD(updateColnos);
}

static void fc__outSetOperationStmt(StringInfo fc_str, const SetOperationStmt *fc_node)
{
	WRITE_NODE_TYPE("SETOPERATIONSTMT");

	WRITE_ENUM_FIELD(op, SetOperation);
	WRITE_BOOL_FIELD(all);
	WRITE_NODE_FIELD(larg);
	WRITE_NODE_FIELD(rarg);
	WRITE_NODE_FIELD(colTypes);
	WRITE_NODE_FIELD(colTypmods);
	WRITE_NODE_FIELD(colCollations);
	WRITE_NODE_FIELD(groupClauses);
}

static void fc__outRangeTblEntry(StringInfo fc_str, const RangeTblEntry *fc_node)
{
	WRITE_NODE_TYPE("RANGETBLENTRY");

	/* 将别名 + eref 放在首位以使转储更加清晰 */
	WRITE_NODE_FIELD(alias);
	WRITE_NODE_FIELD(eref);
	WRITE_ENUM_FIELD(rtekind, RTEKind);

	switch (fc_node->rtekind)
	{
		case RTE_RELATION:
			WRITE_OID_FIELD(relid);
			WRITE_CHAR_FIELD(relkind);
			WRITE_INT_FIELD(rellockmode);
			WRITE_NODE_FIELD(tablesample);
			break;
		case RTE_SUBQUERY:
			WRITE_NODE_FIELD(subquery);
			WRITE_BOOL_FIELD(security_barrier);
			break;
		case RTE_JOIN:
			WRITE_ENUM_FIELD(jointype, JoinType);
			WRITE_INT_FIELD(joinmergedcols);
			WRITE_NODE_FIELD(joinaliasvars);
			WRITE_NODE_FIELD(joinleftcols);
			WRITE_NODE_FIELD(joinrightcols);
			WRITE_NODE_FIELD(join_using_alias);
			break;
		case RTE_FUNCTION:
			WRITE_NODE_FIELD(functions);
			WRITE_BOOL_FIELD(funcordinality);
			break;
		case RTE_TABLEFUNC:
			WRITE_NODE_FIELD(tablefunc);
			break;
		case RTE_VALUES:
			WRITE_NODE_FIELD(values_lists);
			WRITE_NODE_FIELD(coltypes);
			WRITE_NODE_FIELD(coltypmods);
			WRITE_NODE_FIELD(colcollations);
			break;
		case RTE_CTE:
			WRITE_STRING_FIELD(ctename);
			WRITE_UINT_FIELD(ctelevelsup);
			WRITE_BOOL_FIELD(self_reference);
			WRITE_NODE_FIELD(coltypes);
			WRITE_NODE_FIELD(coltypmods);
			WRITE_NODE_FIELD(colcollations);
			break;
		case RTE_NAMEDTUPLESTORE:
			WRITE_STRING_FIELD(enrname);
			WRITE_FLOAT_FIELD(enrtuples, "%.0f");
			WRITE_OID_FIELD(relid);
			WRITE_NODE_FIELD(coltypes);
			WRITE_NODE_FIELD(coltypmods);
			WRITE_NODE_FIELD(colcollations);
			break;
		case RTE_RESULT:
			/* 没有额外的字段 */
			break;
		default:
			elog(ERROR, "unrecognized RTE kind: %d", (int) fc_node->rtekind);
			break;
	}

	WRITE_BOOL_FIELD(lateral);
	WRITE_BOOL_FIELD(inh);
	WRITE_BOOL_FIELD(inFromCl);
	WRITE_UINT_FIELD(requiredPerms);
	WRITE_OID_FIELD(checkAsUser);
	WRITE_BITMAPSET_FIELD(selectedCols);
	WRITE_BITMAPSET_FIELD(insertedCols);
	WRITE_BITMAPSET_FIELD(updatedCols);
	WRITE_BITMAPSET_FIELD(extraUpdatedCols);
	WRITE_NODE_FIELD(securityQuals);
}

static void fc__outRangeTblFunction(StringInfo fc_str, const RangeTblFunction *fc_node)
{
	WRITE_NODE_TYPE("RANGETBLFUNCTION");

	WRITE_NODE_FIELD(funcexpr);
	WRITE_INT_FIELD(funccolcount);
	WRITE_NODE_FIELD(funccolnames);
	WRITE_NODE_FIELD(funccoltypes);
	WRITE_NODE_FIELD(funccoltypmods);
	WRITE_NODE_FIELD(funccolcollations);
	WRITE_BITMAPSET_FIELD(funcparams);
}

static void fc__outTableSampleClause(StringInfo fc_str, const TableSampleClause *fc_node)
{
	WRITE_NODE_TYPE("TABLESAMPLECLAUSE");

	WRITE_OID_FIELD(tsmhandler);
	WRITE_NODE_FIELD(args);
	WRITE_NODE_FIELD(repeatable);
}

static void fc__outA_Expr(StringInfo fc_str, const A_Expr *fc_node)
{
	WRITE_NODE_TYPE("AEXPR");

	switch (fc_node->kind)
	{
		case AEXPR_OP:
			appendStringInfoChar(fc_str, ' ');
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_OP_ANY:
			appendStringInfoChar(fc_str, ' ');
			WRITE_NODE_FIELD(name);
			appendStringInfoString(fc_str, " ANY ");
			break;
		case AEXPR_OP_ALL:
			appendStringInfoChar(fc_str, ' ');
			WRITE_NODE_FIELD(name);
			appendStringInfoString(fc_str, " ALL ");
			break;
		case AEXPR_DISTINCT:
			appendStringInfoString(fc_str, " DISTINCT ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_NOT_DISTINCT:
			appendStringInfoString(fc_str, " NOT_DISTINCT ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_NULLIF:
			appendStringInfoString(fc_str, " NULLIF ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_IN:
			appendStringInfoString(fc_str, " IN ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_LIKE:
			appendStringInfoString(fc_str, " LIKE ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_ILIKE:
			appendStringInfoString(fc_str, " ILIKE ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_SIMILAR:
			appendStringInfoString(fc_str, " SIMILAR ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_BETWEEN:
			appendStringInfoString(fc_str, " BETWEEN ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_NOT_BETWEEN:
			appendStringInfoString(fc_str, " NOT_BETWEEN ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_BETWEEN_SYM:
			appendStringInfoString(fc_str, " BETWEEN_SYM ");
			WRITE_NODE_FIELD(name);
			break;
		case AEXPR_NOT_BETWEEN_SYM:
			appendStringInfoString(fc_str, " NOT_BETWEEN_SYM ");
			WRITE_NODE_FIELD(name);
			break;
		default:
			appendStringInfoString(fc_str, " ??");
			break;
	}

	WRITE_NODE_FIELD(lexpr);
	WRITE_NODE_FIELD(rexpr);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outInteger(StringInfo fc_str, const Integer *fc_node)
{
	appendStringInfo(fc_str, "%d", fc_node->ival);
}

static void fc__outFloat(StringInfo fc_str, const Float *fc_node)
{
	/*
	 * 我们假设值是有效的数字字面量，因此不需要引号。
	 */
	appendStringInfoString(fc_str, fc_node->fval);
}

static void fc__outBoolean(StringInfo fc_str, const Boolean *fc_node)
{
	appendStringInfoString(fc_str, fc_node->boolval ? "true" : "false");
}

static void fc__outString(StringInfo fc_str, const String *fc_node)
{
	/*
	 * 我们使用 outToken 来实现字符串内容的转义，但不希望
	 * 对空字符串进行处理。
	 */
	appendStringInfoChar(fc_str, '"');
	if (fc_node->sval[0] != '\0')
		outToken(fc_str, fc_node->sval);
	appendStringInfoChar(fc_str, '"');
}

static void fc__outBitString(StringInfo fc_str, const BitString *fc_node)
{
	/* 内部表示已经有前导 'b' */
	appendStringInfoString(fc_str, fc_node->bsval);
}

static void fc__outColumnRef(StringInfo fc_str, const ColumnRef *fc_node)
{
	WRITE_NODE_TYPE("COLUMNREF");

	WRITE_NODE_FIELD(fields);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outParamRef(StringInfo fc_str, const ParamRef *fc_node)
{
	WRITE_NODE_TYPE("PARAMREF");

	WRITE_INT_FIELD(number);
	WRITE_LOCATION_FIELD(location);
}

/*
 * 在原始解析树中发现的节点类型（出于调试目的支持）
 */

static void fc__outRawStmt(StringInfo fc_str, const RawStmt *fc_node)
{
	WRITE_NODE_TYPE("RAWSTMT");

	WRITE_NODE_FIELD(stmt);
	WRITE_LOCATION_FIELD(stmt_location);
	WRITE_INT_FIELD(stmt_len);
}

static void fc__outA_Const(StringInfo fc_str, const A_Const *fc_node)
{
	WRITE_NODE_TYPE("A_CONST");

	if (fc_node->isnull)
		appendStringInfoString(fc_str, " NULL");
	else
	{
		appendStringInfoString(fc_str, " :val ");
		outNode(fc_str, &fc_node->val);
	}
	WRITE_LOCATION_FIELD(location);
}

static void fc__outA_Star(StringInfo fc_str, const A_Star *fc_node)
{
	WRITE_NODE_TYPE("A_STAR");
}

static void fc__outA_Indices(StringInfo fc_str, const A_Indices *fc_node)
{
	WRITE_NODE_TYPE("A_INDICES");

	WRITE_BOOL_FIELD(is_slice);
	WRITE_NODE_FIELD(lidx);
	WRITE_NODE_FIELD(uidx);
}

static void fc__outA_Indirection(StringInfo fc_str, const A_Indirection *fc_node)
{
	WRITE_NODE_TYPE("A_INDIRECTION");

	WRITE_NODE_FIELD(arg);
	WRITE_NODE_FIELD(indirection);
}

static void fc__outA_ArrayExpr(StringInfo fc_str, const A_ArrayExpr *fc_node)
{
	WRITE_NODE_TYPE("A_ARRAYEXPR");

	WRITE_NODE_FIELD(elements);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outResTarget(StringInfo fc_str, const ResTarget *fc_node)
{
	WRITE_NODE_TYPE("RESTARGET");

	WRITE_STRING_FIELD(name);
	WRITE_NODE_FIELD(indirection);
	WRITE_NODE_FIELD(val);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outMultiAssignRef(StringInfo fc_str, const MultiAssignRef *fc_node)
{
	WRITE_NODE_TYPE("MULTIASSIGNREF");

	WRITE_NODE_FIELD(source);
	WRITE_INT_FIELD(colno);
	WRITE_INT_FIELD(ncolumns);
}

static void fc__outSortBy(StringInfo fc_str, const SortBy *fc_node)
{
	WRITE_NODE_TYPE("SORTBY");

	WRITE_NODE_FIELD(node);
	WRITE_ENUM_FIELD(sortby_dir, SortByDir);
	WRITE_ENUM_FIELD(sortby_nulls, SortByNulls);
	WRITE_NODE_FIELD(useOp);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outWindowDef(StringInfo fc_str, const WindowDef *fc_node)
{
	WRITE_NODE_TYPE("WINDOWDEF");

	WRITE_STRING_FIELD(name);
	WRITE_STRING_FIELD(refname);
	WRITE_NODE_FIELD(partitionClause);
	WRITE_NODE_FIELD(orderClause);
	WRITE_INT_FIELD(frameOptions);
	WRITE_NODE_FIELD(startOffset);
	WRITE_NODE_FIELD(endOffset);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outRangeSubselect(StringInfo fc_str, const RangeSubselect *fc_node)
{
	WRITE_NODE_TYPE("RANGESUBSELECT");

	WRITE_BOOL_FIELD(lateral);
	WRITE_NODE_FIELD(subquery);
	WRITE_NODE_FIELD(alias);
}

static void fc__outRangeFunction(StringInfo fc_str, const RangeFunction *fc_node)
{
	WRITE_NODE_TYPE("RANGEFUNCTION");

	WRITE_BOOL_FIELD(lateral);
	WRITE_BOOL_FIELD(ordinality);
	WRITE_BOOL_FIELD(is_rowsfrom);
	WRITE_NODE_FIELD(functions);
	WRITE_NODE_FIELD(alias);
	WRITE_NODE_FIELD(coldeflist);
}

static void fc__outRangeTableSample(StringInfo fc_str, const RangeTableSample *fc_node)
{
	WRITE_NODE_TYPE("RANGETABLESAMPLE");

	WRITE_NODE_FIELD(relation);
	WRITE_NODE_FIELD(method);
	WRITE_NODE_FIELD(args);
	WRITE_NODE_FIELD(repeatable);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outRangeTableFunc(StringInfo fc_str, const RangeTableFunc *fc_node)
{
	WRITE_NODE_TYPE("RANGETABLEFUNC");

	WRITE_BOOL_FIELD(lateral);
	WRITE_NODE_FIELD(docexpr);
	WRITE_NODE_FIELD(rowexpr);
	WRITE_NODE_FIELD(namespaces);
	WRITE_NODE_FIELD(columns);
	WRITE_NODE_FIELD(alias);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outRangeTableFuncCol(StringInfo fc_str, const RangeTableFuncCol *fc_node)
{
	WRITE_NODE_TYPE("RANGETABLEFUNCCOL");

	WRITE_STRING_FIELD(colname);
	WRITE_NODE_FIELD(typeName);
	WRITE_BOOL_FIELD(for_ordinality);
	WRITE_BOOL_FIELD(is_not_null);
	WRITE_NODE_FIELD(colexpr);
	WRITE_NODE_FIELD(coldefexpr);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outConstraint(StringInfo fc_str, const Constraint *fc_node)
{
	WRITE_NODE_TYPE("CONSTRAINT");

	WRITE_STRING_FIELD(conname);
	WRITE_BOOL_FIELD(deferrable);
	WRITE_BOOL_FIELD(initdeferred);
	WRITE_LOCATION_FIELD(location);

	appendStringInfoString(fc_str, " :contype ");
	switch (fc_node->contype)
	{
		case CONSTR_NULL:
			appendStringInfoString(fc_str, "NULL");
			break;

		case CONSTR_NOTNULL:
			appendStringInfoString(fc_str, "NOT_NULL");
			break;

		case CONSTR_DEFAULT:
			appendStringInfoString(fc_str, "DEFAULT");
			WRITE_NODE_FIELD(raw_expr);
			WRITE_STRING_FIELD(cooked_expr);
			break;

		case CONSTR_IDENTITY:
			appendStringInfoString(fc_str, "IDENTITY");
			WRITE_NODE_FIELD(options);
			WRITE_CHAR_FIELD(generated_when);
			break;

		case CONSTR_GENERATED:
			appendStringInfoString(fc_str, "GENERATED");
			WRITE_NODE_FIELD(raw_expr);
			WRITE_STRING_FIELD(cooked_expr);
			WRITE_CHAR_FIELD(generated_when);
			break;

		case CONSTR_CHECK:
			appendStringInfoString(fc_str, "CHECK");
			WRITE_BOOL_FIELD(is_no_inherit);
			WRITE_NODE_FIELD(raw_expr);
			WRITE_STRING_FIELD(cooked_expr);
			WRITE_BOOL_FIELD(skip_validation);
			WRITE_BOOL_FIELD(initially_valid);
			break;

		case CONSTR_PRIMARY:
			appendStringInfoString(fc_str, "PRIMARY_KEY");
			WRITE_NODE_FIELD(keys);
			WRITE_NODE_FIELD(including);
			WRITE_NODE_FIELD(options);
			WRITE_STRING_FIELD(indexname);
			WRITE_STRING_FIELD(indexspace);
			WRITE_BOOL_FIELD(reset_default_tblspc);
			/* access_method 和 where_clause 目前未使用 */
			break;

		case CONSTR_UNIQUE:
			appendStringInfoString(fc_str, "UNIQUE");
			WRITE_BOOL_FIELD(nulls_not_distinct);
			WRITE_NODE_FIELD(keys);
			WRITE_NODE_FIELD(including);
			WRITE_NODE_FIELD(options);
			WRITE_STRING_FIELD(indexname);
			WRITE_STRING_FIELD(indexspace);
			WRITE_BOOL_FIELD(reset_default_tblspc);
			/* access_method 和 where_clause 目前未使用 */
			break;

		case CONSTR_EXCLUSION:
			appendStringInfoString(fc_str, "EXCLUSION");
			WRITE_NODE_FIELD(exclusions);
			WRITE_NODE_FIELD(including);
			WRITE_NODE_FIELD(options);
			WRITE_STRING_FIELD(indexname);
			WRITE_STRING_FIELD(indexspace);
			WRITE_BOOL_FIELD(reset_default_tblspc);
			WRITE_STRING_FIELD(access_method);
			WRITE_NODE_FIELD(where_clause);
			break;

		case CONSTR_FOREIGN:
			appendStringInfoString(fc_str, "FOREIGN_KEY");
			WRITE_NODE_FIELD(pktable);
			WRITE_NODE_FIELD(fk_attrs);
			WRITE_NODE_FIELD(pk_attrs);
			WRITE_CHAR_FIELD(fk_matchtype);
			WRITE_CHAR_FIELD(fk_upd_action);
			WRITE_CHAR_FIELD(fk_del_action);
			WRITE_NODE_FIELD(fk_del_set_cols);
			WRITE_NODE_FIELD(old_conpfeqop);
			WRITE_OID_FIELD(old_pktable_oid);
			WRITE_BOOL_FIELD(skip_validation);
			WRITE_BOOL_FIELD(initially_valid);
			break;

		case CONSTR_ATTR_DEFERRABLE:
			appendStringInfoString(fc_str, "ATTR_DEFERRABLE");
			break;

		case CONSTR_ATTR_NOT_DEFERRABLE:
			appendStringInfoString(fc_str, "ATTR_NOT_DEFERRABLE");
			break;

		case CONSTR_ATTR_DEFERRED:
			appendStringInfoString(fc_str, "ATTR_DEFERRED");
			break;

		case CONSTR_ATTR_IMMEDIATE:
			appendStringInfoString(fc_str, "ATTR_IMMEDIATE");
			break;

		default:
			appendStringInfo(fc_str, "<unrecognized_constraint %d>",
							 (int) fc_node->contype);
			break;
	}
}

static void fc__outForeignKeyCacheInfo(StringInfo fc_str, const ForeignKeyCacheInfo *fc_node)
{
	WRITE_NODE_TYPE("FOREIGNKEYCACHEINFO");

	WRITE_OID_FIELD(conoid);
	WRITE_OID_FIELD(conrelid);
	WRITE_OID_FIELD(confrelid);
	WRITE_INT_FIELD(nkeys);
	WRITE_ATTRNUMBER_ARRAY(conkey, fc_node->nkeys);
	WRITE_ATTRNUMBER_ARRAY(confkey, fc_node->nkeys);
	WRITE_OID_ARRAY(conpfeqop, fc_node->nkeys);
}

static void fc__outPartitionElem(StringInfo fc_str, const PartitionElem *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONELEM");

	WRITE_STRING_FIELD(name);
	WRITE_NODE_FIELD(expr);
	WRITE_NODE_FIELD(collation);
	WRITE_NODE_FIELD(opclass);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outPartitionSpec(StringInfo fc_str, const PartitionSpec *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONSPEC");

	WRITE_STRING_FIELD(strategy);
	WRITE_NODE_FIELD(partParams);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outPartitionBoundSpec(StringInfo fc_str, const PartitionBoundSpec *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONBOUNDSPEC");

	WRITE_CHAR_FIELD(strategy);
	WRITE_BOOL_FIELD(is_default);
	WRITE_INT_FIELD(modulus);
	WRITE_INT_FIELD(remainder);
	WRITE_NODE_FIELD(listdatums);
	WRITE_NODE_FIELD(lowerdatums);
	WRITE_NODE_FIELD(upperdatums);
	WRITE_LOCATION_FIELD(location);
}

static void fc__outPartitionRangeDatum(StringInfo fc_str, const PartitionRangeDatum *fc_node)
{
	WRITE_NODE_TYPE("PARTITIONRANGEDATUM");

	WRITE_ENUM_FIELD(kind, PartitionRangeDatumKind);
	WRITE_NODE_FIELD(value);
	WRITE_LOCATION_FIELD(location);
}

/*
 * outNode -
 *	  将节点转换为 ascii 字符串并附加到 'str'
 */
void outNode(StringInfo fc_str, const void *fc_obj)
{
	/* 防止因过于复杂的表达式导致栈溢出 */
	check_stack_depth();

	if (fc_obj == NULL)
		appendStringInfoString(fc_str, "<>");
	else if (IsA(fc_obj, List) || IsA(fc_obj, IntList) || IsA(fc_obj, OidList))
		fc__outList(fc_str, fc_obj);
	/* nodeRead 不希望看到这些 {}！ */
	else if (IsA(fc_obj, Integer))
		fc__outInteger(fc_str, (Integer *) fc_obj);
	else if (IsA(fc_obj, Float))
		fc__outFloat(fc_str, (Float *) fc_obj);
	else if (IsA(fc_obj, Boolean))
		fc__outBoolean(fc_str, (Boolean *) fc_obj);
	else if (IsA(fc_obj, String))
		fc__outString(fc_str, (String *) fc_obj);
	else if (IsA(fc_obj, BitString))
		fc__outBitString(fc_str, (BitString *) fc_obj);
	else
	{
		appendStringInfoChar(fc_str, '{');
		switch (nodeTag(fc_obj))
		{
			case T_PlannedStmt:
				fc__outPlannedStmt(fc_str, fc_obj);
				break;
			case T_Plan:
				fc__outPlan(fc_str, fc_obj);
				break;
			case T_Result:
				fc__outResult(fc_str, fc_obj);
				break;
			case T_ProjectSet:
				fc__outProjectSet(fc_str, fc_obj);
				break;
			case T_ModifyTable:
				fc__outModifyTable(fc_str, fc_obj);
				break;
			case T_Append:
				fc__outAppend(fc_str, fc_obj);
				break;
			case T_MergeAppend:
				fc__outMergeAppend(fc_str, fc_obj);
				break;
			case T_RecursiveUnion:
				fc__outRecursiveUnion(fc_str, fc_obj);
				break;
			case T_BitmapAnd:
				fc__outBitmapAnd(fc_str, fc_obj);
				break;
			case T_BitmapOr:
				fc__outBitmapOr(fc_str, fc_obj);
				break;
			case T_Gather:
				fc__outGather(fc_str, fc_obj);
				break;
			case T_GatherMerge:
				fc__outGatherMerge(fc_str, fc_obj);
				break;
			case T_Scan:
				fc__outScan(fc_str, fc_obj);
				break;
			case T_SeqScan:
				fc__outSeqScan(fc_str, fc_obj);
				break;
			case T_SampleScan:
				fc__outSampleScan(fc_str, fc_obj);
				break;
			case T_IndexScan:
				fc__outIndexScan(fc_str, fc_obj);
				break;
			case T_IndexOnlyScan:
				fc__outIndexOnlyScan(fc_str, fc_obj);
				break;
			case T_BitmapIndexScan:
				fc__outBitmapIndexScan(fc_str, fc_obj);
				break;
			case T_BitmapHeapScan:
				fc__outBitmapHeapScan(fc_str, fc_obj);
				break;
			case T_TidScan:
				fc__outTidScan(fc_str, fc_obj);
				break;
			case T_TidRangeScan:
				fc__outTidRangeScan(fc_str, fc_obj);
				break;
			case T_SubqueryScan:
				fc__outSubqueryScan(fc_str, fc_obj);
				break;
			case T_FunctionScan:
				fc__outFunctionScan(fc_str, fc_obj);
				break;
			case T_TableFuncScan:
				fc__outTableFuncScan(fc_str, fc_obj);
				break;
			case T_ValuesScan:
				fc__outValuesScan(fc_str, fc_obj);
				break;
			case T_CteScan:
				fc__outCteScan(fc_str, fc_obj);
				break;
			case T_NamedTuplestoreScan:
				fc__outNamedTuplestoreScan(fc_str, fc_obj);
				break;
			case T_WorkTableScan:
				fc__outWorkTableScan(fc_str, fc_obj);
				break;
			case T_ForeignScan:
				fc__outForeignScan(fc_str, fc_obj);
				break;
			case T_CustomScan:
				fc__outCustomScan(fc_str, fc_obj);
				break;
			case T_Join:
				fc__outJoin(fc_str, fc_obj);
				break;
			case T_NestLoop:
				fc__outNestLoop(fc_str, fc_obj);
				break;
			case T_MergeJoin:
				fc__outMergeJoin(fc_str, fc_obj);
				break;
			case T_HashJoin:
				fc__outHashJoin(fc_str, fc_obj);
				break;
			case T_Agg:
				fc__outAgg(fc_str, fc_obj);
				break;
			case T_WindowAgg:
				fc__outWindowAgg(fc_str, fc_obj);
				break;
			case T_Group:
				fc__outGroup(fc_str, fc_obj);
				break;
			case T_Material:
				fc__outMaterial(fc_str, fc_obj);
				break;
			case T_Memoize:
				fc__outMemoize(fc_str, fc_obj);
				break;
			case T_Sort:
				fc__outSort(fc_str, fc_obj);
				break;
			case T_IncrementalSort:
				fc__outIncrementalSort(fc_str, fc_obj);
				break;
			case T_Unique:
				fc__outUnique(fc_str, fc_obj);
				break;
			case T_Hash:
				fc__outHash(fc_str, fc_obj);
				break;
			case T_SetOp:
				fc__outSetOp(fc_str, fc_obj);
				break;
			case T_LockRows:
				fc__outLockRows(fc_str, fc_obj);
				break;
			case T_Limit:
				fc__outLimit(fc_str, fc_obj);
				break;
			case T_NestLoopParam:
				fc__outNestLoopParam(fc_str, fc_obj);
				break;
			case T_PlanRowMark:
				fc__outPlanRowMark(fc_str, fc_obj);
				break;
			case T_PartitionPruneInfo:
				fc__outPartitionPruneInfo(fc_str, fc_obj);
				break;
			case T_PartitionedRelPruneInfo:
				fc__outPartitionedRelPruneInfo(fc_str, fc_obj);
				break;
			case T_PartitionPruneStepOp:
				fc__outPartitionPruneStepOp(fc_str, fc_obj);
				break;
			case T_PartitionPruneStepCombine:
				fc__outPartitionPruneStepCombine(fc_str, fc_obj);
				break;
			case T_PlanInvalItem:
				fc__outPlanInvalItem(fc_str, fc_obj);
				break;
			case T_Alias:
				fc__outAlias(fc_str, fc_obj);
				break;
			case T_RangeVar:
				fc__outRangeVar(fc_str, fc_obj);
				break;
			case T_TableFunc:
				fc__outTableFunc(fc_str, fc_obj);
				break;
			case T_IntoClause:
				fc__outIntoClause(fc_str, fc_obj);
				break;
			case T_Var:
				fc__outVar(fc_str, fc_obj);
				break;
			case T_Const:
				fc__outConst(fc_str, fc_obj);
				break;
			case T_Param:
				fc__outParam(fc_str, fc_obj);
				break;
			case T_Aggref:
				fc__outAggref(fc_str, fc_obj);
				break;
			case T_GroupingFunc:
				fc__outGroupingFunc(fc_str, fc_obj);
				break;
			case T_WindowFunc:
				fc__outWindowFunc(fc_str, fc_obj);
				break;
			case T_SubscriptingRef:
				fc__outSubscriptingRef(fc_str, fc_obj);
				break;
			case T_FuncExpr:
				fc__outFuncExpr(fc_str, fc_obj);
				break;
			case T_NamedArgExpr:
				fc__outNamedArgExpr(fc_str, fc_obj);
				break;
			case T_OpExpr:
				fc__outOpExpr(fc_str, fc_obj);
				break;
			case T_DistinctExpr:
				fc__outDistinctExpr(fc_str, fc_obj);
				break;
			case T_NullIfExpr:
				fc__outNullIfExpr(fc_str, fc_obj);
				break;
			case T_ScalarArrayOpExpr:
				fc__outScalarArrayOpExpr(fc_str, fc_obj);
				break;
			case T_BoolExpr:
				fc__outBoolExpr(fc_str, fc_obj);
				break;
			case T_SubLink:
				fc__outSubLink(fc_str, fc_obj);
				break;
			case T_SubPlan:
				fc__outSubPlan(fc_str, fc_obj);
				break;
			case T_AlternativeSubPlan:
				fc__outAlternativeSubPlan(fc_str, fc_obj);
				break;
			case T_FieldSelect:
				fc__outFieldSelect(fc_str, fc_obj);
				break;
			case T_FieldStore:
				fc__outFieldStore(fc_str, fc_obj);
				break;
			case T_RelabelType:
				fc__outRelabelType(fc_str, fc_obj);
				break;
			case T_CoerceViaIO:
				fc__outCoerceViaIO(fc_str, fc_obj);
				break;
			case T_ArrayCoerceExpr:
				fc__outArrayCoerceExpr(fc_str, fc_obj);
				break;
			case T_ConvertRowtypeExpr:
				fc__outConvertRowtypeExpr(fc_str, fc_obj);
				break;
			case T_CollateExpr:
				fc__outCollateExpr(fc_str, fc_obj);
				break;
			case T_CaseExpr:
				fc__outCaseExpr(fc_str, fc_obj);
				break;
			case T_CaseWhen:
				fc__outCaseWhen(fc_str, fc_obj);
				break;
			case T_CaseTestExpr:
				fc__outCaseTestExpr(fc_str, fc_obj);
				break;
			case T_ArrayExpr:
				fc__outArrayExpr(fc_str, fc_obj);
				break;
			case T_RowExpr:
				fc__outRowExpr(fc_str, fc_obj);
				break;
			case T_RowCompareExpr:
				fc__outRowCompareExpr(fc_str, fc_obj);
				break;
			case T_CoalesceExpr:
				fc__outCoalesceExpr(fc_str, fc_obj);
				break;
			case T_MinMaxExpr:
				fc__outMinMaxExpr(fc_str, fc_obj);
				break;
			case T_SQLValueFunction:
				fc__outSQLValueFunction(fc_str, fc_obj);
				break;
			case T_XmlExpr:
				fc__outXmlExpr(fc_str, fc_obj);
				break;
			case T_NullTest:
				fc__outNullTest(fc_str, fc_obj);
				break;
			case T_BooleanTest:
				fc__outBooleanTest(fc_str, fc_obj);
				break;
			case T_CoerceToDomain:
				fc__outCoerceToDomain(fc_str, fc_obj);
				break;
			case T_CoerceToDomainValue:
				fc__outCoerceToDomainValue(fc_str, fc_obj);
				break;
			case T_SetToDefault:
				fc__outSetToDefault(fc_str, fc_obj);
				break;
			case T_CurrentOfExpr:
				fc__outCurrentOfExpr(fc_str, fc_obj);
				break;
			case T_NextValueExpr:
				fc__outNextValueExpr(fc_str, fc_obj);
				break;
			case T_InferenceElem:
				fc__outInferenceElem(fc_str, fc_obj);
				break;
			case T_TargetEntry:
				fc__outTargetEntry(fc_str, fc_obj);
				break;
			case T_RangeTblRef:
				fc__outRangeTblRef(fc_str, fc_obj);
				break;
			case T_JoinExpr:
				fc__outJoinExpr(fc_str, fc_obj);
				break;
			case T_FromExpr:
				fc__outFromExpr(fc_str, fc_obj);
				break;
			case T_OnConflictExpr:
				fc__outOnConflictExpr(fc_str, fc_obj);
				break;
			case T_Path:
				fc__outPath(fc_str, fc_obj);
				break;
			case T_IndexPath:
				fc__outIndexPath(fc_str, fc_obj);
				break;
			case T_BitmapHeapPath:
				fc__outBitmapHeapPath(fc_str, fc_obj);
				break;
			case T_BitmapAndPath:
				fc__outBitmapAndPath(fc_str, fc_obj);
				break;
			case T_BitmapOrPath:
				fc__outBitmapOrPath(fc_str, fc_obj);
				break;
			case T_TidPath:
				fc__outTidPath(fc_str, fc_obj);
				break;
			case T_TidRangePath:
				fc__outTidRangePath(fc_str, fc_obj);
				break;
			case T_SubqueryScanPath:
				fc__outSubqueryScanPath(fc_str, fc_obj);
				break;
			case T_ForeignPath:
				fc__outForeignPath(fc_str, fc_obj);
				break;
			case T_CustomPath:
				fc__outCustomPath(fc_str, fc_obj);
				break;
			case T_AppendPath:
				fc__outAppendPath(fc_str, fc_obj);
				break;
			case T_MergeAppendPath:
				fc__outMergeAppendPath(fc_str, fc_obj);
				break;
			case T_GroupResultPath:
				fc__outGroupResultPath(fc_str, fc_obj);
				break;
			case T_MaterialPath:
				fc__outMaterialPath(fc_str, fc_obj);
				break;
			case T_MemoizePath:
				fc__outMemoizePath(fc_str, fc_obj);
				break;
			case T_UniquePath:
				fc__outUniquePath(fc_str, fc_obj);
				break;
			case T_GatherPath:
				fc__outGatherPath(fc_str, fc_obj);
				break;
			case T_ProjectionPath:
				fc__outProjectionPath(fc_str, fc_obj);
				break;
			case T_ProjectSetPath:
				fc__outProjectSetPath(fc_str, fc_obj);
				break;
			case T_SortPath:
				fc__outSortPath(fc_str, fc_obj);
				break;
			case T_IncrementalSortPath:
				fc__outIncrementalSortPath(fc_str, fc_obj);
				break;
			case T_GroupPath:
				fc__outGroupPath(fc_str, fc_obj);
				break;
			case T_UpperUniquePath:
				fc__outUpperUniquePath(fc_str, fc_obj);
				break;
			case T_AggPath:
				fc__outAggPath(fc_str, fc_obj);
				break;
			case T_GroupingSetsPath:
				fc__outGroupingSetsPath(fc_str, fc_obj);
				break;
			case T_MinMaxAggPath:
				fc__outMinMaxAggPath(fc_str, fc_obj);
				break;
			case T_WindowAggPath:
				fc__outWindowAggPath(fc_str, fc_obj);
				break;
			case T_SetOpPath:
				fc__outSetOpPath(fc_str, fc_obj);
				break;
			case T_RecursiveUnionPath:
				fc__outRecursiveUnionPath(fc_str, fc_obj);
				break;
			case T_LockRowsPath:
				fc__outLockRowsPath(fc_str, fc_obj);
				break;
			case T_ModifyTablePath:
				fc__outModifyTablePath(fc_str, fc_obj);
				break;
			case T_LimitPath:
				fc__outLimitPath(fc_str, fc_obj);
				break;
			case T_GatherMergePath:
				fc__outGatherMergePath(fc_str, fc_obj);
				break;
			case T_NestPath:
				fc__outNestPath(fc_str, fc_obj);
				break;
			case T_MergePath:
				fc__outMergePath(fc_str, fc_obj);
				break;
			case T_HashPath:
				fc__outHashPath(fc_str, fc_obj);
				break;
			case T_PlannerGlobal:
				fc__outPlannerGlobal(fc_str, fc_obj);
				break;
			case T_PlannerInfo:
				fc__outPlannerInfo(fc_str, fc_obj);
				break;
			case T_RelOptInfo:
				fc__outRelOptInfo(fc_str, fc_obj);
				break;
			case T_IndexOptInfo:
				fc__outIndexOptInfo(fc_str, fc_obj);
				break;
			case T_ForeignKeyOptInfo:
				fc__outForeignKeyOptInfo(fc_str, fc_obj);
				break;
			case T_EquivalenceClass:
				fc__outEquivalenceClass(fc_str, fc_obj);
				break;
			case T_EquivalenceMember:
				fc__outEquivalenceMember(fc_str, fc_obj);
				break;
			case T_PathKey:
				fc__outPathKey(fc_str, fc_obj);
				break;
			case T_PathTarget:
				fc__outPathTarget(fc_str, fc_obj);
				break;
			case T_ParamPathInfo:
				fc__outParamPathInfo(fc_str, fc_obj);
				break;
			case T_RestrictInfo:
				fc__outRestrictInfo(fc_str, fc_obj);
				break;
			case T_IndexClause:
				fc__outIndexClause(fc_str, fc_obj);
				break;
			case T_PlaceHolderVar:
				fc__outPlaceHolderVar(fc_str, fc_obj);
				break;
			case T_SpecialJoinInfo:
				fc__outSpecialJoinInfo(fc_str, fc_obj);
				break;
			case T_AppendRelInfo:
				fc__outAppendRelInfo(fc_str, fc_obj);
				break;
			case T_RowIdentityVarInfo:
				fc__outRowIdentityVarInfo(fc_str, fc_obj);
				break;
			case T_PlaceHolderInfo:
				fc__outPlaceHolderInfo(fc_str, fc_obj);
				break;
			case T_MinMaxAggInfo:
				fc__outMinMaxAggInfo(fc_str, fc_obj);
				break;
			case T_PlannerParamItem:
				fc__outPlannerParamItem(fc_str, fc_obj);
				break;
			case T_RollupData:
				fc__outRollupData(fc_str, fc_obj);
				break;
			case T_GroupingSetData:
				fc__outGroupingSetData(fc_str, fc_obj);
				break;
			case T_StatisticExtInfo:
				fc__outStatisticExtInfo(fc_str, fc_obj);
				break;
			case T_ExtensibleNode:
				fc__outExtensibleNode(fc_str, fc_obj);
				break;
			case T_CreateStmt:
				fc__outCreateStmt(fc_str, fc_obj);
				break;
			case T_CreateForeignTableStmt:
				fc__outCreateForeignTableStmt(fc_str, fc_obj);
				break;
			case T_ImportForeignSchemaStmt:
				fc__outImportForeignSchemaStmt(fc_str, fc_obj);
				break;
			case T_IndexStmt:
				fc__outIndexStmt(fc_str, fc_obj);
				break;
			case T_CreateStatsStmt:
				fc__outCreateStatsStmt(fc_str, fc_obj);
				break;
			case T_AlterStatsStmt:
				fc__outAlterStatsStmt(fc_str, fc_obj);
				break;
			case T_NotifyStmt:
				fc__outNotifyStmt(fc_str, fc_obj);
				break;
			case T_DeclareCursorStmt:
				fc__outDeclareCursorStmt(fc_str, fc_obj);
				break;
			case T_SelectStmt:
				fc__outSelectStmt(fc_str, fc_obj);
				break;
			case T_ReturnStmt:
				fc__outReturnStmt(fc_str, fc_obj);
				break;
			case T_PLAssignStmt:
				fc__outPLAssignStmt(fc_str, fc_obj);
				break;
			case T_ColumnDef:
				fc__outColumnDef(fc_str, fc_obj);
				break;
			case T_TypeName:
				fc__outTypeName(fc_str, fc_obj);
				break;
			case T_TypeCast:
				fc__outTypeCast(fc_str, fc_obj);
				break;
			case T_CollateClause:
				fc__outCollateClause(fc_str, fc_obj);
				break;
			case T_IndexElem:
				fc__outIndexElem(fc_str, fc_obj);
				break;
			case T_StatsElem:
				fc__outStatsElem(fc_str, fc_obj);
				break;
			case T_Query:
				fc__outQuery(fc_str, fc_obj);
				break;
			case T_WithCheckOption:
				fc__outWithCheckOption(fc_str, fc_obj);
				break;
			case T_SortGroupClause:
				fc__outSortGroupClause(fc_str, fc_obj);
				break;
			case T_GroupingSet:
				fc__outGroupingSet(fc_str, fc_obj);
				break;
			case T_WindowClause:
				fc__outWindowClause(fc_str, fc_obj);
				break;
			case T_RowMarkClause:
				fc__outRowMarkClause(fc_str, fc_obj);
				break;
			case T_WithClause:
				fc__outWithClause(fc_str, fc_obj);
				break;
			case T_CTESearchClause:
				fc__outCTESearchClause(fc_str, fc_obj);
				break;
			case T_CTECycleClause:
				fc__outCTECycleClause(fc_str, fc_obj);
				break;
			case T_CommonTableExpr:
				fc__outCommonTableExpr(fc_str, fc_obj);
				break;
			case T_MergeWhenClause:
				fc__outMergeWhenClause(fc_str, fc_obj);
				break;
			case T_MergeAction:
				fc__outMergeAction(fc_str, fc_obj);
				break;
			case T_SetOperationStmt:
				fc__outSetOperationStmt(fc_str, fc_obj);
				break;
			case T_RangeTblEntry:
				fc__outRangeTblEntry(fc_str, fc_obj);
				break;
			case T_RangeTblFunction:
				fc__outRangeTblFunction(fc_str, fc_obj);
				break;
			case T_TableSampleClause:
				fc__outTableSampleClause(fc_str, fc_obj);
				break;
			case T_A_Expr:
				fc__outA_Expr(fc_str, fc_obj);
				break;
			case T_ColumnRef:
				fc__outColumnRef(fc_str, fc_obj);
				break;
			case T_ParamRef:
				fc__outParamRef(fc_str, fc_obj);
				break;
			case T_RawStmt:
				fc__outRawStmt(fc_str, fc_obj);
				break;
			case T_A_Const:
				fc__outA_Const(fc_str, fc_obj);
				break;
			case T_A_Star:
				fc__outA_Star(fc_str, fc_obj);
				break;
			case T_A_Indices:
				fc__outA_Indices(fc_str, fc_obj);
				break;
			case T_A_Indirection:
				fc__outA_Indirection(fc_str, fc_obj);
				break;
			case T_A_ArrayExpr:
				fc__outA_ArrayExpr(fc_str, fc_obj);
				break;
			case T_ResTarget:
				fc__outResTarget(fc_str, fc_obj);
				break;
			case T_MultiAssignRef:
				fc__outMultiAssignRef(fc_str, fc_obj);
				break;
			case T_SortBy:
				fc__outSortBy(fc_str, fc_obj);
				break;
			case T_WindowDef:
				fc__outWindowDef(fc_str, fc_obj);
				break;
			case T_RangeSubselect:
				fc__outRangeSubselect(fc_str, fc_obj);
				break;
			case T_RangeFunction:
				fc__outRangeFunction(fc_str, fc_obj);
				break;
			case T_RangeTableSample:
				fc__outRangeTableSample(fc_str, fc_obj);
				break;
			case T_RangeTableFunc:
				fc__outRangeTableFunc(fc_str, fc_obj);
				break;
			case T_RangeTableFuncCol:
				fc__outRangeTableFuncCol(fc_str, fc_obj);
				break;
			case T_Constraint:
				fc__outConstraint(fc_str, fc_obj);
				break;
			case T_FuncCall:
				fc__outFuncCall(fc_str, fc_obj);
				break;
			case T_DefElem:
				fc__outDefElem(fc_str, fc_obj);
				break;
			case T_TableLikeClause:
				fc__outTableLikeClause(fc_str, fc_obj);
				break;
			case T_LockingClause:
				fc__outLockingClause(fc_str, fc_obj);
				break;
			case T_XmlSerialize:
				fc__outXmlSerialize(fc_str, fc_obj);
				break;
			case T_ForeignKeyCacheInfo:
				fc__outForeignKeyCacheInfo(fc_str, fc_obj);
				break;
			case T_TriggerTransition:
				fc__outTriggerTransition(fc_str, fc_obj);
				break;
			case T_PartitionElem:
				fc__outPartitionElem(fc_str, fc_obj);
				break;
			case T_PartitionSpec:
				fc__outPartitionSpec(fc_str, fc_obj);
				break;
			case T_PartitionBoundSpec:
				fc__outPartitionBoundSpec(fc_str, fc_obj);
				break;
			case T_PartitionRangeDatum:
				fc__outPartitionRangeDatum(fc_str, fc_obj);
				break;

			default:

				/*
				 * 这应该是一个错误，但能够转储结构体，对 outNode 只理解部分内容来说太有用了。
				 */
				elog(WARNING, "could not dump unrecognized node type: %d",
					 (int) nodeTag(fc_obj));
				break;
		}
		appendStringInfoChar(fc_str, '}');
	}
}

/*
 * nodeToString -
 *	   返回 Node 的 ASCII 表示作为一个 palloc'd 字符串
 */
char * nodeToString(const void *fc_obj)
{
	StringInfoData fc_str;

	/* 请参阅 stringinfo.h 以获取对此操作的解释 */
	initStringInfo(&fc_str);
	outNode(&fc_str, fc_obj);
	return fc_str.data;
}

/*
 * bmsToString -
 *	   返回 Bitmapset 的 ASCII 表示作为一个 palloc'd 字符串
 */
char * bmsToString(const Bitmapset *fc_bms)
{
	StringInfoData fc_str;

	/* 请参阅 stringinfo.h 以获取对此操作的解释 */
	initStringInfo(&fc_str);
	outBitmapset(&fc_str, fc_bms);
	return fc_str.data;
}
